Merge "Rename hasFullTracking to hasIsFullTracking (frameworks/base)"
diff --git a/Android.bp b/Android.bp
index ea7ced9..effd7ce 100644
--- a/Android.bp
+++ b/Android.bp
@@ -235,6 +235,7 @@
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb-V1.2-java-constants",
+ "android.hardware.usb.gadget-V1-java",
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.usb.gadget-V1.1-java",
"android.hardware.usb.gadget-V1.2-java",
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index a976de3..2ab8ac0 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -58,6 +58,19 @@
+ "180℃で揚げ、衣がきつね色になったら引き上げて、油を切る。"
+ "盛り付けて出来上がり。";
+ public static final String KO_TEXT_SHORT = "모든 인류 구성원의 천부의 존엄성과 동등하고 양도할 수 없는 권리를";
+ // About 530 chars
+ public static final String KO_TEXT_LONG = "모든 인류 구성원의 천부의 존엄성과 동등하고 양도할 수 없는 권리를"
+ + " 인정하는 것이 세계의 자유, 정의 및 평화의 기초이며, 인권에 대한 무시와 경멸이 인류의 양심을 격분시키는"
+ + " 만행을 초래하였으며, 인간이 언론과 신앙의 자유, 그리고 공포와 결핍으로부터의 자유를 누릴 수 있는 세계의"
+ + " 도래가 모든 사람들의 지고한 열망으로서 천명되어 왔으며, 인간이 폭정과 억압에 대항하는 마지막 수단으로서"
+ + " 반란을 일으키도록 강요받지 않으려면, 법에 의한 통치에 의하여 인권이 보호되어야 하는 것이 필수적이며,"
+ + "국가간에 우호관계의 발전을 증진하는 것이 필수적이며, 국제연합의 모든 사람들은 그 헌장에서 기본적 인권,"
+ + " 인간의 존엄과 가치, 그리고 남녀의 동등한 권리에 대한 신념을 재확인하였으며, 보다 폭넓은 "
+ + "자유속에서 사회적 진보와 보다 나은 생활수준을 증진하기로 다짐하였고, 회원국들은 국제연합과 협력하여 인권과"
+ + " 기본적 자유의 보편적 존중과 준수를 증진할 것을 스스로 서약하였으며, 이러한 권리와 자유에 대한 공통의"
+ + " 이해가 이 서약의 완전한 이행을 위하여 가장 중요하므로,";
+
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -560,4 +573,118 @@
.build();
}
}
+
+ @Test
+ public void testCreate_KOText_Phrase_Short() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_SHORT;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_Phrase_Long() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_Phrase_LongLong() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG.repeat(20); // 250 * 20 = 7000 chars
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_NoPhrase_Short() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_SHORT;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_NoPhrase_Long() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_NoPhrase_LongLong() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG.repeat(20); // 520 * 20 = 10400 chars
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/UserVisibleJobSummary.java b/apex/jobscheduler/framework/java/android/app/job/UserVisibleJobSummary.java
index 311a9b2..ba79c30 100644
--- a/apex/jobscheduler/framework/java/android/app/job/UserVisibleJobSummary.java
+++ b/apex/jobscheduler/framework/java/android/app/job/UserVisibleJobSummary.java
@@ -30,6 +30,8 @@
*/
public class UserVisibleJobSummary implements Parcelable {
private final int mCallingUid;
+ @NonNull
+ private final String mCallingPackageName;
private final int mSourceUserId;
@NonNull
private final String mSourcePackageName;
@@ -37,9 +39,11 @@
private final String mNamespace;
private final int mJobId;
- public UserVisibleJobSummary(int callingUid, int sourceUserId,
- @NonNull String sourcePackageName, String namespace, int jobId) {
+ public UserVisibleJobSummary(int callingUid, @NonNull String callingPackageName,
+ int sourceUserId, @NonNull String sourcePackageName,
+ @Nullable String namespace, int jobId) {
mCallingUid = callingUid;
+ mCallingPackageName = callingPackageName;
mSourceUserId = sourceUserId;
mSourcePackageName = sourcePackageName;
mNamespace = namespace;
@@ -48,12 +52,18 @@
protected UserVisibleJobSummary(Parcel in) {
mCallingUid = in.readInt();
+ mCallingPackageName = in.readString();
mSourceUserId = in.readInt();
mSourcePackageName = in.readString();
mNamespace = in.readString();
mJobId = in.readInt();
}
+ @NonNull
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
public int getCallingUid() {
return mCallingUid;
}
@@ -62,6 +72,7 @@
return mJobId;
}
+ @Nullable
public String getNamespace() {
return mNamespace;
}
@@ -70,6 +81,7 @@
return mSourceUserId;
}
+ @NonNull
public String getSourcePackageName() {
return mSourcePackageName;
}
@@ -80,6 +92,7 @@
if (!(o instanceof UserVisibleJobSummary)) return false;
UserVisibleJobSummary that = (UserVisibleJobSummary) o;
return mCallingUid == that.mCallingUid
+ && mCallingPackageName.equals(that.mCallingPackageName)
&& mSourceUserId == that.mSourceUserId
&& mSourcePackageName.equals(that.mSourcePackageName)
&& Objects.equals(mNamespace, that.mNamespace)
@@ -90,6 +103,7 @@
public int hashCode() {
int result = 0;
result = 31 * result + mCallingUid;
+ result = 31 * result + mCallingPackageName.hashCode();
result = 31 * result + mSourceUserId;
result = 31 * result + mSourcePackageName.hashCode();
if (mNamespace != null) {
@@ -103,6 +117,7 @@
public String toString() {
return "UserVisibleJobSummary{"
+ "callingUid=" + mCallingUid
+ + ", callingPackageName='" + mCallingPackageName + "'"
+ ", sourceUserId=" + mSourceUserId
+ ", sourcePackageName='" + mSourcePackageName + "'"
+ ", namespace=" + mNamespace
@@ -118,6 +133,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mCallingUid);
+ dest.writeString(mCallingPackageName);
dest.writeInt(mSourceUserId);
dest.writeString(mSourcePackageName);
dest.writeString(mNamespace);
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 4a3a6d9..581ea7a 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -16,11 +16,15 @@
package android.app.tare;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.content.Context;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Provides access to the resource economy service.
*
@@ -91,10 +95,38 @@
}
}
- public static final String KEY_ENABLE_TARE = "enable_tare";
+
+ public static final int ENABLED_MODE_OFF = 0;
+ public static final int ENABLED_MODE_ON = 1;
+ /**
+ * Go through the motions, tracking events, updating balances and other TARE state values,
+ * but don't use TARE to affect actual device behavior.
+ */
+ public static final int ENABLED_MODE_SHADOW = 2;
+
+ /** @hide */
+ @IntDef(prefix = {"ENABLED_MODE_"}, value = {
+ ENABLED_MODE_OFF,
+ ENABLED_MODE_ON,
+ ENABLED_MODE_SHADOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EnabledMode {
+ }
+
+ public static String enabledModeToString(@EnabledMode int mode) {
+ switch (mode) {
+ case ENABLED_MODE_OFF: return "ENABLED_MODE_OFF";
+ case ENABLED_MODE_ON: return "ENABLED_MODE_ON";
+ case ENABLED_MODE_SHADOW: return "ENABLED_MODE_SHADOW";
+ default: return "ENABLED_MODE_" + mode;
+ }
+ }
+
+ public static final String KEY_ENABLE_TARE_MODE = "enable_tare_mode";
public static final String KEY_ENABLE_POLICY_ALARM = "enable_policy_alarm";
public static final String KEY_ENABLE_POLICY_JOB_SCHEDULER = "enable_policy_job";
- public static final boolean DEFAULT_ENABLE_TARE = true;
+ public static final int DEFAULT_ENABLE_TARE_MODE = ENABLED_MODE_OFF;
public static final boolean DEFAULT_ENABLE_POLICY_ALARM = true;
public static final boolean DEFAULT_ENABLE_POLICY_JOB_SCHEDULER = true;
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 442c130..217b8b6 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -17,12 +17,9 @@
package com.android.server.job;
import android.annotation.Nullable;
-import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.util.proto.ProtoOutputStream;
-import java.util.List;
-
/**
* JobScheduler local system service interface.
* {@hide} Only for use within the system server.
@@ -30,11 +27,6 @@
public interface JobSchedulerInternal {
/**
- * Returns a list of pending jobs scheduled by the system service.
- */
- List<JobInfo> getSystemScheduledPendingJobs();
-
- /**
* Cancel the jobs for a given uid (e.g. when app data is cleared)
*
* @param includeProxiedJobs Include jobs scheduled for this UID by other apps
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index fab5b5f..ad406a1 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -158,17 +158,11 @@
boolean mForceAllAppStandbyForSmallBattery;
/**
- * True if the forced app standby feature is enabled in settings
- */
- @GuardedBy("mLock")
- boolean mForcedAppStandbyEnabled;
-
- /**
* A lock-free set of (uid, packageName) pairs in background restricted mode.
*
* <p>
- * It's bascially shadowing the {@link #mRunAnyRestrictedPackages} together with
- * the {@link #mForcedAppStandbyEnabled} - mutations on them would result in copy-on-write.
+ * It's basically shadowing the {@link #mRunAnyRestrictedPackages}, any mutations on it would
+ * result in copy-on-write.
* </p>
*/
volatile Set<Pair<Integer, String>> mBackgroundRestrictedUidPackages = Collections.emptySet();
@@ -200,10 +194,9 @@
int TEMP_EXEMPTION_LIST_CHANGED = 5;
int EXEMPTED_BUCKET_CHANGED = 6;
int FORCE_ALL_CHANGED = 7;
- int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
- int IS_UID_ACTIVE_CACHED = 9;
- int IS_UID_ACTIVE_RAW = 10;
+ int IS_UID_ACTIVE_CACHED = 8;
+ int IS_UID_ACTIVE_RAW = 9;
}
private final StatLogger mStatLogger = new StatLogger(new String[] {
@@ -215,7 +208,6 @@
"TEMP_EXEMPTION_LIST_CHANGED",
"EXEMPTED_BUCKET_CHANGED",
"FORCE_ALL_CHANGED",
- "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED",
"IS_UID_ACTIVE_CACHED",
"IS_UID_ACTIVE_RAW",
@@ -228,18 +220,10 @@
}
void register() {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
- false, this);
-
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
}
- boolean isForcedAppStandbyEnabled() {
- return injectGetGlobalSettingInt(Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
- }
-
boolean isForcedAppStandbyForSmallBatteryEnabled() {
return injectGetGlobalSettingInt(
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
@@ -247,21 +231,7 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
- if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
- final boolean enabled = isForcedAppStandbyEnabled();
- synchronized (mLock) {
- if (mForcedAppStandbyEnabled == enabled) {
- return;
- }
- mForcedAppStandbyEnabled = enabled;
- updateBackgroundRestrictedUidPackagesLocked();
- if (DEBUG) {
- Slog.d(TAG, "Forced app standby feature flag changed: "
- + mForcedAppStandbyEnabled);
- }
- }
- mHandler.notifyForcedAppStandbyFeatureFlagChanged();
- } else if (Settings.Global.getUriFor(
+ if (Settings.Global.getUriFor(
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
synchronized (mLock) {
@@ -515,7 +485,6 @@
mFlagsObserver = new FeatureFlagsObserver();
mFlagsObserver.register();
- mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
mForceAllAppStandbyForSmallBattery =
mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
mStandbyTracker = new StandbyTracker();
@@ -636,14 +605,10 @@
/**
* Update the {@link #mBackgroundRestrictedUidPackages} upon mutations on
- * {@link #mRunAnyRestrictedPackages} or {@link #mForcedAppStandbyEnabled}.
+ * {@link #mRunAnyRestrictedPackages}.
*/
@GuardedBy("mLock")
private void updateBackgroundRestrictedUidPackagesLocked() {
- if (!mForcedAppStandbyEnabled) {
- mBackgroundRestrictedUidPackages = Collections.emptySet();
- return;
- }
Set<Pair<Integer, String>> fasUidPkgs = new ArraySet<>();
for (int i = 0, size = mRunAnyRestrictedPackages.size(); i < size; i++) {
fasUidPkgs.add(mRunAnyRestrictedPackages.valueAt(i));
@@ -821,13 +786,14 @@
private class MyHandler extends Handler {
private static final int MSG_UID_ACTIVE_STATE_CHANGED = 0;
+ // Unused ids 1, 2.
private static final int MSG_RUN_ANY_CHANGED = 3;
private static final int MSG_ALL_UNEXEMPTED = 4;
private static final int MSG_ALL_EXEMPTION_LIST_CHANGED = 5;
private static final int MSG_TEMP_EXEMPTION_LIST_CHANGED = 6;
private static final int MSG_FORCE_ALL_CHANGED = 7;
private static final int MSG_USER_REMOVED = 8;
- private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9;
+ // Unused id 9.
private static final int MSG_EXEMPTED_BUCKET_CHANGED = 10;
private static final int MSG_AUTO_RESTRICTED_BUCKET_FEATURE_FLAG_CHANGED = 11;
@@ -867,11 +833,6 @@
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
- public void notifyForcedAppStandbyFeatureFlagChanged() {
- removeMessages(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED);
- obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
- }
-
public void notifyExemptedBucketChanged() {
removeMessages(MSG_EXEMPTED_BUCKET_CHANGED);
obtainMessage(MSG_EXEMPTED_BUCKET_CHANGED).sendToTarget();
@@ -966,22 +927,6 @@
mStatLogger.logDurationStat(Stats.FORCE_ALL_CHANGED, start);
return;
- case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
- // Feature flag for forced app standby changed.
- final boolean unblockAlarms;
- synchronized (mLock) {
- unblockAlarms = !mForcedAppStandbyEnabled;
- }
- for (Listener l : cloneListeners()) {
- l.updateAllJobs();
- if (unblockAlarms) {
- l.unblockAllUnrestrictedAlarms();
- }
- }
- mStatLogger.logDurationStat(
- Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start);
- return;
-
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
return;
@@ -1164,8 +1109,7 @@
// If apps will be put into restricted standby bucket automatically on user-forced
// app standby, instead of blocking alarms completely, let the restricted standby bucket
// policy take care of it.
- return (mForcedAppStandbyEnabled
- && !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
+ return (!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
&& isRunAnyRestrictedLocked(uid, packageName));
}
}
@@ -1210,8 +1154,7 @@
// If apps will be put into restricted standby bucket automatically on user-forced
// app standby, instead of blocking jobs completely, let the restricted standby bucket
// policy take care of it.
- if (mForcedAppStandbyEnabled
- && !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
+ if (!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
&& isRunAnyRestrictedLocked(uid, packageName)) {
return true;
}
@@ -1321,8 +1264,6 @@
pw.println("Current AppStateTracker State:");
pw.increaseIndent();
- pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
-
pw.print("Force all apps standby: ");
pw.println(isForceAllAppsStandbyEnabled());
@@ -1400,8 +1341,6 @@
synchronized (mLock) {
final long token = proto.start(fieldId);
- proto.write(AppStateTrackerProto.FORCED_APP_STANDBY_FEATURE_ENABLED,
- mForcedAppStandbyEnabled);
proto.write(AppStateTrackerProto.FORCE_ALL_APPS_STANDBY,
isForceAllAppsStandbyEnabled());
proto.write(AppStateTrackerProto.IS_SMALL_BATTERY_DEVICE, isSmallBatteryDevice());
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 0fa5764..4daf12a 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2538,6 +2538,7 @@
final Bundle mostRecentDeliveryOptions = BroadcastOptions.makeBasic()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeferUntilActive(true)
.toBundle();
mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e41eb00..d7163d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -77,6 +77,7 @@
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.app.role.RoleManager;
+import android.app.tare.EconomyManager;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -852,7 +853,7 @@
public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED;
- public boolean USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE == 1;
+ public int USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE;
/**
* The amount of temporary reserve quota to give apps on receiving the
@@ -892,7 +893,7 @@
AlarmManagerEconomicPolicy.POLICY_ALARM);
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
updateTareSettings(
- economyManagerInternal.isEnabled(AlarmManagerEconomicPolicy.POLICY_ALARM));
+ economyManagerInternal.getEnabledMode(AlarmManagerEconomicPolicy.POLICY_ALARM));
}
public void updateAllowWhileIdleWhitelistDurationLocked() {
@@ -1065,18 +1066,19 @@
}
@Override
- public void onTareEnabledStateChanged(boolean isTareEnabled) {
- updateTareSettings(isTareEnabled);
+ public void onTareEnabledModeChanged(@EconomyManager.EnabledMode int enabledMode) {
+ updateTareSettings(enabledMode);
}
- private void updateTareSettings(boolean isTareEnabled) {
+ private void updateTareSettings(int enabledMode) {
synchronized (mLock) {
- if (USE_TARE_POLICY != isTareEnabled) {
- USE_TARE_POLICY = isTareEnabled;
+ if (USE_TARE_POLICY != enabledMode) {
+ USE_TARE_POLICY = enabledMode;
final boolean changed = mAlarmStore.updateAlarmDeliveries(alarm -> {
final boolean standbyChanged = adjustDeliveryTimeBasedOnBucketLocked(alarm);
final boolean tareChanged = adjustDeliveryTimeBasedOnTareLocked(alarm);
- if (USE_TARE_POLICY) {
+ if (USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) {
+ // Only register listeners if we're going to be acting on the policy.
registerTareListener(alarm);
} else {
mEconomyManagerInternal.unregisterAffordabilityChangeListener(
@@ -1086,7 +1088,7 @@
}
return standbyChanged || tareChanged;
});
- if (!USE_TARE_POLICY) {
+ if (USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON) {
// Remove the cached values so we don't accidentally use them when TARE is
// re-enabled.
mAffordabilityCache.clear();
@@ -2526,7 +2528,8 @@
*/
private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) {
final long nowElapsed = mInjector.getElapsedRealtimeMillis();
- if (mConstants.USE_TARE_POLICY || isExemptFromAppStandby(alarm) || mAppStandbyParole) {
+ if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON
+ || isExemptFromAppStandby(alarm) || mAppStandbyParole) {
return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
}
@@ -2586,7 +2589,7 @@
*/
private boolean adjustDeliveryTimeBasedOnTareLocked(Alarm alarm) {
final long nowElapsed = mInjector.getElapsedRealtimeMillis();
- if (!mConstants.USE_TARE_POLICY
+ if (mConstants.USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON
|| isExemptFromTare(alarm) || hasEnoughWealthLocked(alarm)) {
return alarm.setPolicyElapsed(TARE_POLICY_INDEX, nowElapsed);
}
@@ -2596,7 +2599,8 @@
}
private void registerTareListener(Alarm alarm) {
- if (!mConstants.USE_TARE_POLICY) {
+ if (mConstants.USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON) {
+ // Only register listeners if we're going to be acting on the policy.
return;
}
mEconomyManagerInternal.registerAffordabilityChangeListener(
@@ -2607,7 +2611,7 @@
/** Unregister the TARE listener associated with the alarm if it's no longer needed. */
@GuardedBy("mLock")
private void maybeUnregisterTareListenerLocked(Alarm alarm) {
- if (!mConstants.USE_TARE_POLICY) {
+ if (mConstants.USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON) {
return;
}
final EconomyManagerInternal.ActionBill bill = TareBill.getAppropriateBill(alarm);
@@ -3126,7 +3130,7 @@
mConstants.dump(pw);
pw.println();
- if (mConstants.USE_TARE_POLICY) {
+ if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) {
pw.println("TARE details:");
pw.increaseIndent();
@@ -4500,7 +4504,8 @@
}
private void reportAlarmEventToTare(Alarm alarm) {
- if (!mConstants.USE_TARE_POLICY) {
+ // Don't bother reporting events if TARE is completely off.
+ if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_OFF) {
return;
}
final boolean allowWhileIdle =
@@ -4800,12 +4805,16 @@
}
final ArraySet<UserPackage> triggerPackages = new ArraySet<>();
final IntArray wakeupUids = new IntArray();
+ final SparseIntArray countsPerUid = new SparseIntArray();
+ final SparseIntArray wakeupCountsPerUid = new SparseIntArray();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
+ increment(countsPerUid, a.uid);
if (a.wakeup) {
wakeupUids.add(a.uid);
+ increment(wakeupCountsPerUid, a.uid);
}
- if (mConstants.USE_TARE_POLICY) {
+ if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) {
if (!isExemptFromTare(a)) {
triggerPackages.add(UserPackage.of(
UserHandle.getUserId(a.creatorUid),
@@ -4823,14 +4832,15 @@
}
deliverAlarmsLocked(triggerList, nowELAPSED);
mTemporaryQuotaReserve.cleanUpExpiredQuotas(nowELAPSED);
- if (mConstants.USE_TARE_POLICY) {
+ if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) {
reorderAlarmsBasedOnTare(triggerPackages);
} else {
reorderAlarmsBasedOnStandbyBuckets(triggerPackages);
}
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
- MetricsHelper.pushAlarmBatchDelivered(triggerList.size(), wakeUps);
+ logAlarmBatchDelivered(
+ triggerList.size(), wakeUps, countsPerUid, wakeupCountsPerUid);
}
}
@@ -4845,6 +4855,32 @@
}
}
+ private static void increment(SparseIntArray array, int key) {
+ final int index = array.indexOfKey(key);
+ if (index >= 0) {
+ array.setValueAt(index, array.valueAt(index) + 1);
+ } else {
+ array.put(key, 1);
+ }
+ }
+
+ private void logAlarmBatchDelivered(
+ int alarms,
+ int wakeups,
+ SparseIntArray countsPerUid,
+ SparseIntArray wakeupCountsPerUid) {
+ final int[] uids = new int[countsPerUid.size()];
+ final int[] countsArray = new int[countsPerUid.size()];
+ final int[] wakeupCountsArray = new int[countsPerUid.size()];
+ for (int i = 0; i < countsPerUid.size(); i++) {
+ uids[i] = countsPerUid.keyAt(i);
+ countsArray[i] = countsPerUid.valueAt(i);
+ wakeupCountsArray[i] = wakeupCountsPerUid.get(uids[i], 0);
+ }
+ MetricsHelper.pushAlarmBatchDelivered(
+ alarms, wakeups, uids, countsArray, wakeupCountsArray);
+ }
+
/**
* Attribute blame for a WakeLock.
*
@@ -5379,9 +5415,7 @@
@Override
public void unblockAllUnrestrictedAlarms() {
- // Called when:
- // 1. Power exemption list changes,
- // 2. User FAS feature is disabled.
+ // Called when the power exemption list changes.
synchronized (mLock) {
sendAllUnrestrictedPendingBackgroundAlarmsLocked();
}
@@ -5763,12 +5797,7 @@
}
private void incrementAlarmCount(int uid) {
- final int uidIndex = mAlarmsPerUid.indexOfKey(uid);
- if (uidIndex >= 0) {
- mAlarmsPerUid.setValueAt(uidIndex, mAlarmsPerUid.valueAt(uidIndex) + 1);
- } else {
- mAlarmsPerUid.put(uid, 1);
- }
+ increment(mAlarmsPerUid, uid);
}
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
index 28acb45..eb1848d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
@@ -117,10 +117,14 @@
ActivityManager.processStateAmToProto(callerProcState));
}
- static void pushAlarmBatchDelivered(int numAlarms, int wakeups) {
+ static void pushAlarmBatchDelivered(
+ int numAlarms, int wakeups, int[] uids, int[] alarmsPerUid, int[] wakeupAlarmsPerUid) {
FrameworkStatsLog.write(
FrameworkStatsLog.ALARM_BATCH_DELIVERED,
numAlarms,
- wakeups);
+ wakeups,
+ uids,
+ alarmsPerUid,
+ wakeupAlarmsPerUid);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
index 862d8b7..3f46cc4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
@@ -16,6 +16,8 @@
package com.android.server.job;
+import android.app.job.JobParameters;
+
import com.android.server.job.controllers.JobStatus;
/**
@@ -26,8 +28,12 @@
/**
* Callback for when a job is completed.
*
- * @param stopReason The stop reason provided to JobParameters.
- * @param needsReschedule Whether the implementing class should reschedule this job.
+ * @param stopReason The stop reason returned from
+ * {@link JobParameters#getStopReason()}.
+ * @param internalStopReason The stop reason returned from
+ * {@link JobParameters#getInternalStopReasonCode()}.
+ * @param needsReschedule Whether the implementing class should reschedule this job.
*/
- void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule);
+ void onJobCompletedLocked(JobStatus jobStatus, int stopReason, int internalStopReason,
+ boolean needsReschedule);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 43f7279..299cb6c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -43,6 +43,7 @@
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
import android.app.job.UserVisibleJobSummary;
+import android.app.tare.EconomyManager;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.compat.annotation.ChangeId;
@@ -417,7 +418,8 @@
// Load all the constants.
synchronized (mLock) {
mConstants.updateTareSettingsLocked(
- economyManagerInternal.isEnabled(JobSchedulerEconomicPolicy.POLICY_JOB));
+ economyManagerInternal.getEnabledMode(
+ JobSchedulerEconomicPolicy.POLICY_JOB));
}
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
}
@@ -514,8 +516,8 @@
}
@Override
- public void onTareEnabledStateChanged(boolean isTareEnabled) {
- if (mConstants.updateTareSettingsLocked(isTareEnabled)) {
+ public void onTareEnabledModeChanged(@EconomyManager.EnabledMode int enabledMode) {
+ if (mConstants.updateTareSettingsLocked(enabledMode)) {
for (int controller = 0; controller < mControllers.size(); controller++) {
final StateController sc = mControllers.get(controller);
sc.onConstantsUpdatedLocked();
@@ -962,10 +964,11 @@
DEFAULT_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS)));
}
- private boolean updateTareSettingsLocked(boolean isTareEnabled) {
+ private boolean updateTareSettingsLocked(@EconomyManager.EnabledMode int enabledMode) {
boolean changed = false;
- if (USE_TARE_POLICY != isTareEnabled) {
- USE_TARE_POLICY = isTareEnabled;
+ final boolean useTare = enabledMode == EconomyManager.ENABLED_MODE_ON;
+ if (USE_TARE_POLICY != useTare) {
+ USE_TARE_POLICY = useTare;
changed = true;
}
return changed;
@@ -1692,10 +1695,28 @@
}
private void stopUserVisibleJobsInternal(@NonNull String packageName, int userId) {
+ final int packageUid = mLocalPM.getPackageUid(packageName, 0, userId);
+ if (packageUid < 0) {
+ Slog.wtf(TAG, "Asked to stop jobs of an unknown package");
+ return;
+ }
synchronized (mLock) {
mConcurrencyManager.stopUserVisibleJobsLocked(userId, packageName,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP);
+ final ArraySet<JobStatus> jobs = mJobs.getJobsByUid(packageUid);
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ final JobStatus job = jobs.valueAt(i);
+
+ // For now, demote all jobs of the app. However, if the app was only doing work
+ // on behalf of another app and the user wanted just that work to stop, this
+ // unfairly penalizes any other jobs that may be scheduled.
+ // For example, if apps A & B ask app C to do something (thus A & B are "source"
+ // and C is "calling"), but only A's work was under way and the user wanted
+ // to stop only that work, B's jobs would be demoted as well.
+ // TODO(255768978): make it possible to demote only the relevant subset of jobs
+ job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
+ }
}
}
@@ -2349,7 +2370,7 @@
*/
@VisibleForTesting
JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule,
- int internalStopReason) {
+ @JobParameters.StopReason int stopReason, int internalStopReason) {
final long elapsedNowMillis = sElapsedRealtimeClock.millis();
final JobInfo job = failureToReschedule.getJob();
@@ -2357,9 +2378,11 @@
int numFailures = failureToReschedule.getNumFailures();
int numSystemStops = failureToReschedule.getNumSystemStops();
// We should back off slowly if JobScheduler keeps stopping the job,
- // but back off immediately if the issue appeared to be the app's fault.
+ // but back off immediately if the issue appeared to be the app's fault
+ // or the user stopped the job somehow.
if (internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH
- || internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT) {
+ || internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
+ || stopReason == JobParameters.STOP_REASON_USER) {
numFailures++;
} else {
numSystemStops++;
@@ -2390,11 +2413,14 @@
}
delayMillis =
Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
- // TODO(255767350): demote all jobs to regular for user stops so they don't keep privileges
JobStatus newJob = new JobStatus(failureToReschedule,
elapsedNowMillis + delayMillis,
JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops,
failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
+ if (stopReason == JobParameters.STOP_REASON_USER) {
+ // Demote all jobs to regular for user stops so they don't keep privileges.
+ newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
+ }
if (job.isPeriodic()) {
newJob.setOriginalLatestRunTimeElapsed(
failureToReschedule.getOriginalLatestRunTimeElapsed());
@@ -2515,8 +2541,8 @@
* @param needsReschedule Whether the implementing class should reschedule this job.
*/
@Override
- public void onJobCompletedLocked(JobStatus jobStatus, int debugStopReason,
- boolean needsReschedule) {
+ public void onJobCompletedLocked(JobStatus jobStatus, @JobParameters.StopReason int stopReason,
+ int debugStopReason, boolean needsReschedule) {
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reason=" + debugStopReason
+ ", reschedule=" + needsReschedule);
@@ -2543,8 +2569,9 @@
// job so we can transfer any appropriate state over from the previous job when
// we stop it.
final JobStatus rescheduledJob = needsReschedule
- ? getRescheduleJobForFailureLocked(jobStatus, debugStopReason) : null;
+ ? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null;
if (rescheduledJob != null
+ && !rescheduledJob.shouldTreatAsUserInitiatedJob()
&& (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
|| debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) {
rescheduledJob.disallowRunInBatterySaverAndDoze();
@@ -3474,23 +3501,6 @@
final class LocalService implements JobSchedulerInternal {
- /**
- * Returns a list of all pending jobs. A running job is not considered pending. Periodic
- * jobs are always considered pending.
- */
- @Override
- public List<JobInfo> getSystemScheduledPendingJobs() {
- synchronized (mLock) {
- final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
- mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
- if (job.getJob().isPeriodic() || !mConcurrencyManager.isJobRunningLocked(job)) {
- pendingJobs.add(job.getJob());
- }
- });
- return pendingJobs;
- }
- }
-
@Override
public void cancelJobsForUid(int uid, boolean includeProxiedJobs,
@JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index fb5d63e..911744f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -361,7 +361,14 @@
boolean binding = false;
try {
final int bindFlags;
- if (job.shouldTreatAsExpeditedJob()) {
+ if (job.shouldTreatAsUserInitiatedJob()) {
+ // TODO (191785864, 261999509): add an appropriate flag so user-initiated jobs
+ // can bypass data saver
+ bindFlags = Context.BIND_AUTO_CREATE
+ | Context.BIND_ALMOST_PERCEPTIBLE
+ | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
+ | Context.BIND_NOT_APP_COMPONENT_USAGE;
+ } else if (job.shouldTreatAsExpeditedJob()) {
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_ALMOST_PERCEPTIBLE
| Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
@@ -1187,6 +1194,7 @@
applyStoppedReasonLocked(reason);
completedJob = mRunningJob;
final int internalStopReason = mParams.getInternalStopReasonCode();
+ final int stopReason = mParams.getStopReason();
mPreviousJobHadSuccessfulFinish =
(internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
if (!mPreviousJobHadSuccessfulFinish) {
@@ -1207,7 +1215,7 @@
completedJob.hasContentTriggerConstraint(),
completedJob.isRequestedExpeditedJob(),
completedJob.startedAsExpeditedJob,
- mParams.getStopReason(),
+ stopReason,
completedJob.getJob().isPrefetch(),
completedJob.getJob().getPriority(),
completedJob.getEffectivePriority(),
@@ -1260,7 +1268,8 @@
if (completedJob.isUserVisibleJob()) {
mService.informObserversOfUserVisibleJobChange(this, completedJob, false);
}
- mCompletedListener.onJobCompletedLocked(completedJob, internalStopReason, reschedule);
+ mCompletedListener.onJobCompletedLocked(completedJob, stopReason, internalStopReason,
+ reschedule);
mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
index 0a305a2..4f4096f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
@@ -219,6 +219,8 @@
ajq.clear();
mAppJobQueuePool.release(ajq);
} else if (prevTimestamp != ajq.peekNextTimestamp()) {
+ // Removing the job changed the "next timestamp" in the queue, so we need to reinsert
+ // it to fix the ordering.
mOrderedQueues.remove(ajq);
mOrderedQueues.offer(ajq);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 16f5c7f..b87dec1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -98,6 +98,13 @@
~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
| ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
| ConnectivityManager.BLOCKED_REASON_DOZE);
+ // TODO(261999509): allow bypassing data saver & user-restricted. However, when we allow a UI
+ // job to run while data saver restricts the app, we must ensure that we don't run regular
+ // jobs when we put a hole in the data saver wall for the UI job
+ private static final int UNBYPASSABLE_UI_BLOCKED_REASONS =
+ ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
+ | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
+ | ConnectivityManager.BLOCKED_REASON_DOZE);
private static final int UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS =
~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
| ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
@@ -1080,6 +1087,11 @@
Slog.d(TAG, "Using FG bypass for " + jobStatus.getSourceUid());
}
unbypassableBlockedReasons = UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS;
+ } else if (jobStatus.shouldTreatAsUserInitiatedJob()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Using UI bypass for " + jobStatus.getSourceUid());
+ }
+ unbypassableBlockedReasons = UNBYPASSABLE_UI_BLOCKED_REASONS;
} else if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.startedAsExpeditedJob) {
if (DEBUG) {
Slog.d(TAG, "Using EJ bypass for " + jobStatus.getSourceUid());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index a2e8eb4..ce33a8e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -373,6 +373,11 @@
* @hide
*/
public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0;
+ /**
+ * Flag for {@link #mInternalFlags}: this job was stopped by the user for some reason
+ * and is thus considered demoted from whatever privileged state it had in the past.
+ */
+ public static final int INTERNAL_FLAG_DEMOTED_BY_USER = 1 << 1;
/** Minimum difference between start and end time to have flexible constraint */
@VisibleForTesting
@@ -1380,8 +1385,8 @@
* for any reason.
*/
public boolean shouldTreatAsUserInitiatedJob() {
- // TODO(248386641): update implementation to handle loss of privilege
- return getJob().isUserInitiated();
+ return getJob().isUserInitiated()
+ && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0;
}
/**
@@ -1391,7 +1396,8 @@
public UserVisibleJobSummary getUserVisibleJobSummary() {
if (mUserVisibleJobSummary == null) {
mUserVisibleJobSummary = new UserVisibleJobSummary(
- callingUid, getSourceUserId(), getSourcePackageName(),
+ callingUid, getServiceComponent().getPackageName(),
+ getSourceUserId(), getSourcePackageName(),
getNamespace(), getJobId());
}
return mUserVisibleJobSummary;
@@ -1411,16 +1417,18 @@
public boolean canRunInDoze() {
return appHasDozeExemption
|| (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
- || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
|| shouldTreatAsUserInitiatedJob()
- && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
+ // EJs can't run in Doze if we explicitly require that the device is not Dozing.
+ || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
+ && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
}
boolean canRunInBatterySaver() {
return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
- || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
|| shouldTreatAsUserInitiatedJob()
- && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
+ // EJs can't run in Battery Saver if we explicitly require that Battery Saver is off
+ || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
+ && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
}
/** @return true if the constraint was changed, false otherwise. */
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 2b59209..dcc324d 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -16,6 +16,7 @@
package com.android.server.tare;
+import static android.app.tare.EconomyManager.ENABLED_MODE_OFF;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME;
@@ -1111,7 +1112,7 @@
final ActionAffordabilityNote note =
new ActionAffordabilityNote(bill, listener, economicPolicy);
if (actionAffordabilityNotes.add(note)) {
- if (!mIrs.isEnabled()) {
+ if (mIrs.getEnabledMode() == ENABLED_MODE_OFF) {
// When TARE isn't enabled, we always say something is affordable. We also don't
// want to silently drop affordability change listeners in case TARE becomes enabled
// because then clients will be in an ambiguous state.
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
index 716769c..5b305ad 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.tare.EconomyManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -121,7 +122,7 @@
/** Listener for various TARE state changes. */
interface TareStateChangeListener {
- void onTareEnabledStateChanged(boolean isTareEnabled);
+ void onTareEnabledModeChanged(@EconomyManager.EnabledMode int tareEnabledMode);
}
/**
@@ -135,11 +136,13 @@
*/
long getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill);
- /** Returns true if TARE is enabled. */
- boolean isEnabled();
+ /** Returns the current TARE enabled mode. */
+ @EconomyManager.EnabledMode
+ int getEnabledMode();
- /** Returns true if TARE and the specified policy are enabled. */
- boolean isEnabled(@EconomicPolicy.Policy int policyId);
+ /** Returns the current TARE enabled mode for the specified policy. */
+ @EconomyManager.EnabledMode
+ int getEnabledMode(@EconomicPolicy.Policy int policyId);
/**
* Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 08c1a0c..caf72e8 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -16,6 +16,10 @@
package com.android.server.tare;
+import static android.app.tare.EconomyManager.ENABLED_MODE_OFF;
+import static android.app.tare.EconomyManager.ENABLED_MODE_ON;
+import static android.app.tare.EconomyManager.ENABLED_MODE_SHADOW;
+import static android.app.tare.EconomyManager.enabledModeToString;
import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -202,7 +206,8 @@
private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>();
private volatile boolean mHasBattery = true;
- private volatile boolean mIsEnabled;
+ @EconomyManager.EnabledMode
+ private volatile int mEnabledMode;
private volatile int mBootPhase;
private volatile boolean mExemptListLoaded;
// In the range [0,100] to represent 0% to 100% battery.
@@ -474,13 +479,20 @@
}
}
- boolean isEnabled() {
- return mIsEnabled;
+ @EconomyManager.EnabledMode
+ int getEnabledMode() {
+ return mEnabledMode;
}
- boolean isEnabled(int policyId) {
+ @EconomyManager.EnabledMode
+ int getEnabledMode(int policyId) {
synchronized (mLock) {
- return isEnabled() && mCompleteEconomicPolicy.isPolicyEnabled(policyId);
+ // For now, treat enabled policies as using the same enabled mode as full TARE.
+ // TODO: have enabled mode by policy
+ if (mCompleteEconomicPolicy.isPolicyEnabled(policyId)) {
+ return mEnabledMode;
+ }
+ return ENABLED_MODE_OFF;
}
}
@@ -857,7 +869,7 @@
@GuardedBy("mLock")
private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
- if (!mIsEnabled) {
+ if (mEnabledMode == ENABLED_MODE_OFF) {
return;
}
final String pkgName = event.getPackageName();
@@ -1028,7 +1040,7 @@
/** Perform long-running and/or heavy setup work. This should be called off the main thread. */
private void setupHeavyWork() {
- if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+ if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || mEnabledMode == ENABLED_MODE_OFF) {
return;
}
synchronized (mLock) {
@@ -1079,7 +1091,7 @@
}
private void onBootPhaseSystemServicesReady() {
- if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || !mIsEnabled) {
+ if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || mEnabledMode == ENABLED_MODE_OFF) {
return;
}
synchronized (mLock) {
@@ -1101,14 +1113,14 @@
}
private void onBootPhaseThirdPartyAppsCanStart() {
- if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+ if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || mEnabledMode == ENABLED_MODE_OFF) {
return;
}
mHandler.post(this::setupHeavyWork);
}
private void onBootPhaseBootCompleted() {
- if (mBootPhase < PHASE_BOOT_COMPLETED || !mIsEnabled) {
+ if (mBootPhase < PHASE_BOOT_COMPLETED || mEnabledMode == ENABLED_MODE_OFF) {
return;
}
synchronized (mLock) {
@@ -1124,7 +1136,7 @@
}
private void setupEverything() {
- if (!mIsEnabled) {
+ if (mEnabledMode == ENABLED_MODE_OFF) {
return;
}
if (mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
@@ -1139,7 +1151,7 @@
}
private void tearDownEverything() {
- if (mIsEnabled) {
+ if (mEnabledMode != ENABLED_MODE_OFF) {
return;
}
synchronized (mLock) {
@@ -1231,7 +1243,7 @@
case MSG_NOTIFY_STATE_CHANGE_LISTENER: {
final int policy = msg.arg1;
final TareStateChangeListener listener = (TareStateChangeListener) msg.obj;
- listener.onTareEnabledStateChanged(isEnabled(policy));
+ listener.onTareEnabledModeChanged(getEnabledMode(policy));
}
break;
@@ -1246,10 +1258,10 @@
}
final ArraySet<TareStateChangeListener> listeners =
mStateChangeListeners.get(policy);
- final boolean isEnabled = isEnabled(policy);
+ final int enabledMode = getEnabledMode(policy);
for (int p = listeners.size() - 1; p >= 0; --p) {
final TareStateChangeListener listener = listeners.valueAt(p);
- listener.onTareEnabledStateChanged(isEnabled);
+ listener.onTareEnabledModeChanged(enabledMode);
}
}
}
@@ -1382,7 +1394,7 @@
@Override
public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) {
- if (!mIsEnabled) {
+ if (mEnabledMode == ENABLED_MODE_OFF) {
return true;
}
if (isVip(userId, pkgName)) {
@@ -1410,7 +1422,7 @@
@Override
public long getMaxDurationMs(int userId, @NonNull String pkgName,
@NonNull ActionBill bill) {
- if (!mIsEnabled) {
+ if (mEnabledMode == ENABLED_MODE_OFF) {
return FOREVER_MS;
}
if (isVip(userId, pkgName)) {
@@ -1437,19 +1449,19 @@
}
@Override
- public boolean isEnabled() {
- return mIsEnabled;
+ public int getEnabledMode() {
+ return mEnabledMode;
}
@Override
- public boolean isEnabled(int policyId) {
- return InternalResourceService.this.isEnabled(policyId);
+ public int getEnabledMode(int policyId) {
+ return InternalResourceService.this.getEnabledMode(policyId);
}
@Override
public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
@Nullable String tag) {
- if (!mIsEnabled) {
+ if (mEnabledMode == ENABLED_MODE_OFF) {
return;
}
synchronized (mLock) {
@@ -1460,7 +1472,7 @@
@Override
public void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
@Nullable String tag) {
- if (!mIsEnabled) {
+ if (mEnabledMode == ENABLED_MODE_OFF) {
return;
}
synchronized (mLock) {
@@ -1472,7 +1484,7 @@
@Override
public void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
@Nullable String tag) {
- if (!mIsEnabled) {
+ if (mEnabledMode == ENABLED_MODE_OFF) {
return;
}
final long nowElapsed = SystemClock.elapsedRealtime();
@@ -1540,7 +1552,7 @@
continue;
}
switch (name) {
- case EconomyManager.KEY_ENABLE_TARE:
+ case EconomyManager.KEY_ENABLE_TARE_MODE:
updateEnabledStatus();
break;
case KEY_ENABLE_TIP3:
@@ -1567,17 +1579,33 @@
private void updateEnabledStatus() {
// User setting should override DeviceConfig setting.
- final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE,
- EconomyManager.KEY_ENABLE_TARE, EconomyManager.DEFAULT_ENABLE_TARE);
- final boolean isTareEnabled = isTareSupported()
- && Settings.Global.getInt(mContentResolver,
- Settings.Global.ENABLE_TARE, isTareEnabledDC ? 1 : 0) == 1;
- if (mIsEnabled != isTareEnabled) {
- mIsEnabled = isTareEnabled;
- if (mIsEnabled) {
- setupEverything();
- } else {
- tearDownEverything();
+ final int tareEnabledModeDC = DeviceConfig.getInt(DeviceConfig.NAMESPACE_TARE,
+ EconomyManager.KEY_ENABLE_TARE_MODE, EconomyManager.DEFAULT_ENABLE_TARE_MODE);
+ final int tareEnabledModeConfig = isTareSupported()
+ ? Settings.Global.getInt(mContentResolver,
+ Settings.Global.ENABLE_TARE, tareEnabledModeDC)
+ : ENABLED_MODE_OFF;
+ final int enabledMode;
+ if (tareEnabledModeConfig == ENABLED_MODE_OFF
+ || tareEnabledModeConfig == ENABLED_MODE_ON
+ || tareEnabledModeConfig == ENABLED_MODE_SHADOW) {
+ // Config has a valid enabled mode.
+ enabledMode = tareEnabledModeConfig;
+ } else {
+ enabledMode = EconomyManager.DEFAULT_ENABLE_TARE_MODE;
+ }
+ if (mEnabledMode != enabledMode) {
+ // A full change where we've gone from OFF to {SHADOW or ON}, or vie versa.
+ // With this transition, we'll have to set up or tear down.
+ final boolean fullEnableChange =
+ mEnabledMode == ENABLED_MODE_OFF || enabledMode == ENABLED_MODE_OFF;
+ mEnabledMode = enabledMode;
+ if (fullEnableChange) {
+ if (mEnabledMode != ENABLED_MODE_OFF) {
+ setupEverything();
+ } else {
+ tearDownEverything();
+ }
}
mHandler.obtainMessage(
MSG_NOTIFY_STATE_CHANGE_LISTENERS, EconomicPolicy.ALL_POLICIES, 0)
@@ -1592,7 +1620,8 @@
final int oldEnabledPolicies = mCompleteEconomicPolicy.getEnabledPolicyIds();
mCompleteEconomicPolicy.tearDown();
mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this);
- if (mIsEnabled && mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) {
+ if (mEnabledMode != ENABLED_MODE_OFF
+ && mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) {
mCompleteEconomicPolicy.setup(getAllDeviceConfigProperties());
if (minLimit != mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit()
|| maxLimit
@@ -1626,7 +1655,7 @@
}
}
mVipOverrides.clear();
- if (mIsEnabled) {
+ if (mEnabledMode != ENABLED_MODE_OFF) {
mAgent.onVipStatusChangedLocked(changedPkgs);
}
}
@@ -1645,7 +1674,7 @@
mVipOverrides.add(userId, pkgName, newVipState);
}
changed = isVip(userId, pkgName) != wasVip;
- if (mIsEnabled && changed) {
+ if (mEnabledMode != ENABLED_MODE_OFF && changed) {
mAgent.onVipStatusChangedLocked(userId, pkgName);
}
}
@@ -1668,8 +1697,8 @@
return;
}
synchronized (mLock) {
- pw.print("Is enabled: ");
- pw.println(mIsEnabled);
+ pw.print("Enabled mode: ");
+ pw.println(enabledModeToString(mEnabledMode));
pw.print("Current battery level: ");
pw.println(mCurrentBatteryLevel);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 66327fd..08439f3 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -16,6 +16,7 @@
package com.android.server.tare;
+import static android.app.tare.EconomyManager.ENABLED_MODE_OFF;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static com.android.server.tare.TareUtils.appToString;
@@ -662,7 +663,7 @@
// Remove mCleanRunnable callbacks since we're going to clean up the ledgers before
// writing anyway.
TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
- if (!mIrs.isEnabled()) {
+ if (mIrs.getEnabledMode() == ENABLED_MODE_OFF) {
// If it's no longer enabled, we would have cleared all the data in memory and would
// accidentally write an empty file, thus deleting all the history.
return;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 13a2a94..9909764 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -965,17 +965,21 @@
Slog.d(TAG, " Checking idle state for " + packageName
+ " minBucket=" + standbyBucketToString(minBucket));
}
+ final boolean previouslyIdle, stillIdle;
if (minBucket <= STANDBY_BUCKET_ACTIVE) {
// No extra processing needed for ACTIVE or higher since apps can't drop into lower
// buckets.
synchronized (mAppIdleLock) {
+ previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
minBucket, REASON_MAIN_DEFAULT);
+ stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
minBucket, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
+ previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
final AppIdleHistory.AppUsageHistory app =
mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
@@ -1073,11 +1077,17 @@
if (oldBucket != newBucket || predictionLate) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, reason);
+ stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
maybeInformListeners(packageName, userId, elapsedRealtime,
newBucket, reason, false);
+ } else {
+ stillIdle = previouslyIdle;
}
}
}
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(packageName, userId, stillIdle);
+ }
}
/** Returns true if there hasn't been a prediction for the app in a while. */
@@ -1234,8 +1244,9 @@
appHistory.currentBucket, reason, userStartedInteracting);
}
- if (previouslyIdle) {
- notifyBatteryStats(pkg, userId, false);
+ final boolean stillIdle = appHistory.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(pkg, userId, stillIdle);
}
}
@@ -1808,8 +1819,14 @@
reason = REASON_MAIN_FORCED_BY_SYSTEM
| (app.bucketingReason & REASON_SUB_MASK)
| (reason & REASON_SUB_MASK);
+ final boolean previouslyIdle =
+ app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
newBucket, reason, resetTimeout);
+ final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(packageName, userId, stillIdle);
+ }
return;
}
@@ -1910,8 +1927,13 @@
// Make sure we don't put the app in a lower bucket than it's supposed to be in.
newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
+ final boolean previouslyIdle = app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason, resetTimeout);
+ final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(packageName, userId, stillIdle);
+ }
}
maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
}
@@ -2668,7 +2690,9 @@
}
void noteEvent(int event, String packageName, int uid) throws RemoteException {
- mBatteryStats.noteEvent(event, packageName, uid);
+ if (mBatteryStats != null) {
+ mBatteryStats.noteEvent(event, packageName, uid);
+ }
}
PackageManagerInternal getPackageManagerInternal() {
diff --git a/core/api/current.txt b/core/api/current.txt
index fa31409..2cfc257 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -64,6 +64,8 @@
field public static final String BLUETOOTH_SCAN = "android.permission.BLUETOOTH_SCAN";
field public static final String BODY_SENSORS = "android.permission.BODY_SENSORS";
field public static final String BODY_SENSORS_BACKGROUND = "android.permission.BODY_SENSORS_BACKGROUND";
+ field public static final String BODY_SENSORS_WRIST_TEMPERATURE = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE";
+ field public static final String BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND";
field public static final String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED";
field public static final String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
field public static final String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY";
@@ -1898,6 +1900,18 @@
field public static final int system_accent3_700 = 17170522; // 0x106005a
field public static final int system_accent3_800 = 17170523; // 0x106005b
field public static final int system_accent3_900 = 17170524; // 0x106005c
+ field public static final int system_background_dark;
+ field public static final int system_background_light;
+ field public static final int system_control_activated_dark;
+ field public static final int system_control_activated_light;
+ field public static final int system_control_highlight_dark;
+ field public static final int system_control_highlight_light;
+ field public static final int system_control_normal_dark;
+ field public static final int system_control_normal_light;
+ field public static final int system_error_container_dark;
+ field public static final int system_error_container_light;
+ field public static final int system_error_dark;
+ field public static final int system_error_light;
field public static final int system_neutral1_0 = 17170461; // 0x106001d
field public static final int system_neutral1_10 = 17170462; // 0x106001e
field public static final int system_neutral1_100 = 17170464; // 0x1060020
@@ -1924,6 +1938,104 @@
field public static final int system_neutral2_700 = 17170483; // 0x1060033
field public static final int system_neutral2_800 = 17170484; // 0x1060034
field public static final int system_neutral2_900 = 17170485; // 0x1060035
+ field public static final int system_on_background_dark;
+ field public static final int system_on_background_light;
+ field public static final int system_on_error_container_dark;
+ field public static final int system_on_error_container_light;
+ field public static final int system_on_error_dark;
+ field public static final int system_on_error_light;
+ field public static final int system_on_primary_container_dark;
+ field public static final int system_on_primary_container_light;
+ field public static final int system_on_primary_dark;
+ field public static final int system_on_primary_fixed_dark;
+ field public static final int system_on_primary_fixed_light;
+ field public static final int system_on_primary_fixed_variant_dark;
+ field public static final int system_on_primary_fixed_variant_light;
+ field public static final int system_on_primary_light;
+ field public static final int system_on_secondary_container_dark;
+ field public static final int system_on_secondary_container_light;
+ field public static final int system_on_secondary_dark;
+ field public static final int system_on_secondary_fixed_dark;
+ field public static final int system_on_secondary_fixed_light;
+ field public static final int system_on_secondary_fixed_variant_dark;
+ field public static final int system_on_secondary_fixed_variant_light;
+ field public static final int system_on_secondary_light;
+ field public static final int system_on_surface_dark;
+ field public static final int system_on_surface_light;
+ field public static final int system_on_surface_variant_dark;
+ field public static final int system_on_surface_variant_light;
+ field public static final int system_on_tertiary_container_dark;
+ field public static final int system_on_tertiary_container_light;
+ field public static final int system_on_tertiary_dark;
+ field public static final int system_on_tertiary_fixed_dark;
+ field public static final int system_on_tertiary_fixed_light;
+ field public static final int system_on_tertiary_fixed_variant_dark;
+ field public static final int system_on_tertiary_fixed_variant_light;
+ field public static final int system_on_tertiary_light;
+ field public static final int system_outline_dark;
+ field public static final int system_outline_light;
+ field public static final int system_palette_key_color_neutral_dark;
+ field public static final int system_palette_key_color_neutral_light;
+ field public static final int system_palette_key_color_neutral_variant_dark;
+ field public static final int system_palette_key_color_neutral_variant_light;
+ field public static final int system_palette_key_color_primary_dark;
+ field public static final int system_palette_key_color_primary_light;
+ field public static final int system_palette_key_color_secondary_dark;
+ field public static final int system_palette_key_color_secondary_light;
+ field public static final int system_palette_key_color_tertiary_dark;
+ field public static final int system_palette_key_color_tertiary_light;
+ field public static final int system_primary_container_dark;
+ field public static final int system_primary_container_light;
+ field public static final int system_primary_dark;
+ field public static final int system_primary_fixed_dark;
+ field public static final int system_primary_fixed_darker_light;
+ field public static final int system_primary_fixed_light;
+ field public static final int system_primary_fixeder_dark;
+ field public static final int system_primary_light;
+ field public static final int system_secondary_container_dark;
+ field public static final int system_secondary_container_light;
+ field public static final int system_secondary_dark;
+ field public static final int system_secondary_fixed_dark;
+ field public static final int system_secondary_fixed_darker_light;
+ field public static final int system_secondary_fixed_light;
+ field public static final int system_secondary_fixeder_dark;
+ field public static final int system_secondary_light;
+ field public static final int system_surface_bright_dark;
+ field public static final int system_surface_bright_light;
+ field public static final int system_surface_container_dark;
+ field public static final int system_surface_container_high_dark;
+ field public static final int system_surface_container_high_light;
+ field public static final int system_surface_container_highest_dark;
+ field public static final int system_surface_container_highest_light;
+ field public static final int system_surface_container_light;
+ field public static final int system_surface_container_low_dark;
+ field public static final int system_surface_container_low_light;
+ field public static final int system_surface_container_lowest_dark;
+ field public static final int system_surface_container_lowest_light;
+ field public static final int system_surface_dark;
+ field public static final int system_surface_dim_dark;
+ field public static final int system_surface_dim_light;
+ field public static final int system_surface_light;
+ field public static final int system_surface_variant_dark;
+ field public static final int system_surface_variant_light;
+ field public static final int system_tertiary_container_dark;
+ field public static final int system_tertiary_container_light;
+ field public static final int system_tertiary_dark;
+ field public static final int system_tertiary_fixed_dark;
+ field public static final int system_tertiary_fixed_darker_light;
+ field public static final int system_tertiary_fixed_light;
+ field public static final int system_tertiary_fixeder_dark;
+ field public static final int system_tertiary_light;
+ field public static final int system_text_hint_inverse_dark;
+ field public static final int system_text_hint_inverse_light;
+ field public static final int system_text_primary_inverse_dark;
+ field public static final int system_text_primary_inverse_disable_only_dark;
+ field public static final int system_text_primary_inverse_disable_only_light;
+ field public static final int system_text_primary_inverse_light;
+ field public static final int system_text_secondary_and_tertiary_inverse_dark;
+ field public static final int system_text_secondary_and_tertiary_inverse_disabled_dark;
+ field public static final int system_text_secondary_and_tertiary_inverse_disabled_light;
+ field public static final int system_text_secondary_and_tertiary_inverse_light;
field public static final int tab_indicator_text = 17170441; // 0x1060009
field @Deprecated public static final int tertiary_text_dark = 17170448; // 0x1060010
field @Deprecated public static final int tertiary_text_light = 17170449; // 0x1060011
@@ -4319,6 +4431,7 @@
method public void recreate();
method public void registerActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
method public void registerForContextMenu(android.view.View);
+ method @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_CAPTURE) public void registerScreenCaptureCallback(@NonNull java.util.concurrent.Executor, @NonNull android.app.Activity.ScreenCaptureCallback);
method public boolean releaseInstance();
method @Deprecated public final void removeDialog(int);
method public void reportFullyDrawn();
@@ -4404,6 +4517,7 @@
method public void triggerSearch(String, @Nullable android.os.Bundle);
method public void unregisterActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
method public void unregisterForContextMenu(android.view.View);
+ method @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_CAPTURE) public void unregisterScreenCaptureCallback(@NonNull android.app.Activity.ScreenCaptureCallback);
field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1
field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0
field public static final int DEFAULT_KEYS_SEARCH_GLOBAL = 4; // 0x4
@@ -4417,6 +4531,10 @@
field public static final int RESULT_OK = -1; // 0xffffffff
}
+ public static interface Activity.ScreenCaptureCallback {
+ method public void onScreenCaptured();
+ }
+
@Deprecated public class ActivityGroup extends android.app.Activity {
ctor @Deprecated public ActivityGroup();
ctor @Deprecated public ActivityGroup(boolean);
@@ -4839,6 +4957,7 @@
field public static final String OPSTR_ADD_VOICEMAIL = "android:add_voicemail";
field public static final String OPSTR_ANSWER_PHONE_CALLS = "android:answer_phone_calls";
field public static final String OPSTR_BODY_SENSORS = "android:body_sensors";
+ field public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE = "android:body_sensors_wrist_temperature";
field public static final String OPSTR_CALL_PHONE = "android:call_phone";
field public static final String OPSTR_CAMERA = "android:camera";
field public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -6730,6 +6849,7 @@
method public static void writePendingIntentOrNullToParcel(@Nullable android.app.PendingIntent, @NonNull android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
+ field public static final int FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT = 16777216; // 0x1000000
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
@@ -7536,6 +7656,7 @@
method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
method public boolean getCameraDisabled(@Nullable android.content.ComponentName);
method @Deprecated @Nullable public String getCertInstallerPackage(@NonNull android.content.ComponentName) throws java.lang.SecurityException;
+ method @Nullable public android.app.admin.PackagePolicy getCredentialManagerPolicy();
method @Deprecated @Nullable public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(@NonNull android.content.ComponentName);
method @Deprecated public boolean getCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean getCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName);
@@ -7688,6 +7809,7 @@
method @Deprecated public void setCertInstallerPackage(@NonNull android.content.ComponentName, @Nullable String) throws java.lang.SecurityException;
method public void setCommonCriteriaModeEnabled(@NonNull android.content.ComponentName, boolean);
method public void setConfiguredNetworksLockdownState(@NonNull android.content.ComponentName, boolean);
+ method public void setCredentialManagerPolicy(@Nullable android.app.admin.PackagePolicy);
method @Deprecated public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
method @Deprecated public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
method @Deprecated public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
@@ -9258,8 +9380,10 @@
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void addOnAssociationsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.Callback);
+ method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void attachSystemDataTransport(int, @NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws android.companion.DeviceNotAssociatedException;
method @Nullable public android.content.IntentSender buildAssociationCancellationIntent();
method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
+ method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException;
method public void disableSystemDataSync(int, int);
method @Deprecated public void disassociate(@NonNull String);
method public void disassociate(int);
@@ -9271,6 +9395,7 @@
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void removeOnAssociationsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
method public void requestNotificationAccess(android.content.ComponentName);
method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
+ method public void startSystemDataTransfer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.companion.CompanionException>) throws android.companion.DeviceNotAssociatedException;
method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION";
field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
@@ -9296,6 +9421,8 @@
public abstract class CompanionDeviceService extends android.app.Service {
ctor public CompanionDeviceService();
+ method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void attachSystemDataTransport(int, @NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws android.companion.DeviceNotAssociatedException;
+ method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException;
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String);
method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
@@ -9304,6 +9431,9 @@
field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService";
}
+ public class CompanionException extends java.lang.RuntimeException {
+ }
+
public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable {
}
@@ -12730,7 +12860,7 @@
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
- field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
+ field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
@@ -13335,10 +13465,22 @@
field @NonNull public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
}
+ public final class CredentialDescription implements android.os.Parcelable {
+ ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
+ method @NonNull public String getFlattenedRequestString();
+ method @NonNull public String getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
+ }
+
public final class CredentialManager {
method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>);
method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
+ method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest);
+ method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest);
}
public class GetCredentialException extends java.lang.Exception {
@@ -13362,6 +13504,7 @@
method public boolean isSystemProviderRequired();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialOption> CREATOR;
+ field public static final String FLATTENED_REQUEST = "android.credentials.GetCredentialOption.FLATTENED_REQUEST_STRING";
}
public final class GetCredentialRequest implements android.os.Parcelable {
@@ -13387,6 +13530,24 @@
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialResponse> CREATOR;
}
+ public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
+ ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+ ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.Set<android.credentials.CredentialDescription>);
+ method public int describeContents();
+ method @NonNull public java.util.Set<android.credentials.CredentialDescription> getCredentialDescriptions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
+ }
+
+ public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
+ ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+ ctor public UnregisterCredentialDescriptionRequest(@NonNull java.util.Set<android.credentials.CredentialDescription>);
+ method public int describeContents();
+ method @NonNull public java.util.Set<android.credentials.CredentialDescription> getCredentialDescriptions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
+ }
+
}
package android.database {
@@ -19035,15 +19196,15 @@
method public int getSurfaceGroupId();
method @NonNull public java.util.List<android.view.Surface> getSurfaces();
method public int getTimestampBase();
- method public boolean isReadoutTimestampUsed();
+ method public boolean isReadoutTimestampEnabled();
method public void removeSensorPixelModeUsed(int);
method public void removeSurface(@NonNull android.view.Surface);
method public void setDynamicRangeProfile(long);
method public void setMirrorMode(int);
method public void setPhysicalCameraId(@Nullable String);
+ method public void setReadoutTimestampEnabled(boolean);
method public void setStreamUseCase(long);
method public void setTimestampBase(int);
- method public void useReadoutTimestamp(boolean);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int MIRROR_MODE_AUTO = 0; // 0x0
@@ -20902,6 +21063,7 @@
method public void adjustStreamVolume(int, int, int);
method public void adjustSuggestedStreamVolume(int, int, int);
method public void adjustVolume(int, int);
+ method public void adjustVolumeGroupVolume(int, int, int);
method public void clearCommunicationDevice();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public boolean clearPreferredMixerAttributes(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioDeviceInfo);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
@@ -20932,6 +21094,7 @@
method public float getStreamVolumeDb(int, int, int);
method @NonNull public java.util.List<android.media.AudioMixerAttributes> getSupportedMixerAttributes(@NonNull android.media.AudioDeviceInfo);
method @Deprecated public int getVibrateSetting(int);
+ method public int getVolumeGroupIdForAttributes(@NonNull android.media.AudioAttributes);
method @Deprecated public boolean isBluetoothA2dpOn();
method public boolean isBluetoothScoAvailableOffCall();
method @Deprecated public boolean isBluetoothScoOn();
@@ -20945,6 +21108,7 @@
method public boolean isStreamMute(int);
method public boolean isSurroundFormatEnabled(int);
method public boolean isVolumeFixed();
+ method public boolean isVolumeGroupMuted(int);
method @Deprecated public boolean isWiredHeadsetOn();
method public void loadSoundEffects();
method public void playSoundEffect(int);
@@ -21205,7 +21369,8 @@
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
- public final class AudioPresentation {
+ public final class AudioPresentation implements android.os.Parcelable {
+ method public int describeContents();
method public java.util.Map<java.util.Locale,java.lang.String> getLabels();
method public java.util.Locale getLocale();
method public int getMasteringIndication();
@@ -21214,6 +21379,7 @@
method public boolean hasAudioDescription();
method public boolean hasDialogueEnhancement();
method public boolean hasSpokenSubtitles();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CONTENT_COMMENTARY = 5; // 0x5
field public static final int CONTENT_DIALOG = 4; // 0x4
field public static final int CONTENT_EMERGENCY = 6; // 0x6
@@ -21223,11 +21389,14 @@
field public static final int CONTENT_UNKNOWN = -1; // 0xffffffff
field public static final int CONTENT_VISUALLY_IMPAIRED = 2; // 0x2
field public static final int CONTENT_VOICEOVER = 7; // 0x7
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPresentation> CREATOR;
field public static final int MASTERED_FOR_3D = 3; // 0x3
field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
field public static final int MASTERED_FOR_STEREO = 1; // 0x1
field public static final int MASTERED_FOR_SURROUND = 2; // 0x2
field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
+ field public static final int PRESENTATION_ID_UNKNOWN = -1; // 0xffffffff
+ field public static final int PROGRAM_ID_UNKNOWN = -1; // 0xffffffff
}
public static final class AudioPresentation.Builder {
@@ -24026,6 +24195,7 @@
method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
+ method public void showSystemOutputSwitcher();
method public void stop();
method public void transferTo(@NonNull android.media.MediaRoute2Info);
method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
@@ -26859,6 +27029,7 @@
method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String);
method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(@NonNull String);
method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String);
+ method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String, @NonNull android.content.AttributionSource);
field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64
field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190
field public static final int PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK = 300; // 0x12c
@@ -26897,6 +27068,8 @@
method public void layoutSurface(int, int, int, int);
method public void notifyAdResponse(@NonNull android.media.tv.AdResponse);
method public void notifyAitInfoUpdated(@NonNull android.media.tv.AitInfo);
+ method public void notifyAudioPresentationChanged(@NonNull java.util.List<android.media.AudioPresentation>);
+ method public void notifyAudioPresentationSelected(int, int);
method public void notifyBroadcastInfoResponse(@NonNull android.media.tv.BroadcastInfoResponse);
method public void notifyChannelRetuned(android.net.Uri);
method public void notifyContentAllowed();
@@ -26920,6 +27093,7 @@
method public void onRemoveBroadcastInfo(int);
method public void onRequestAd(@NonNull android.media.tv.AdRequest);
method public void onRequestBroadcastInfo(@NonNull android.media.tv.BroadcastInfoRequest);
+ method public boolean onSelectAudioPresentation(int, int);
method public boolean onSelectTrack(int, @Nullable String);
method public abstract void onSetCaptionEnabled(boolean);
method public void onSetInteractiveAppNotificationEnabled(boolean);
@@ -26965,6 +27139,39 @@
method public void onTuned(android.net.Uri);
}
+ public final class TvRecordingInfo implements android.os.Parcelable {
+ ctor public TvRecordingInfo(@NonNull String, long, long, int, @NonNull String, @NonNull String, long, long, @NonNull android.net.Uri, @Nullable android.net.Uri, @NonNull java.util.List<android.media.tv.TvContentRating>, @Nullable android.net.Uri, long, long);
+ method public int describeContents();
+ method @NonNull public android.net.Uri getChannelUri();
+ method @NonNull public java.util.List<android.media.tv.TvContentRating> getContentRatings();
+ method @NonNull public String getDescription();
+ method @NonNull public long getEndPaddingMillis();
+ method @NonNull public String getName();
+ method @Nullable public android.net.Uri getProgramUri();
+ method @IntRange(from=0xffffffff) @NonNull public long getRecordingDurationMillis();
+ method @NonNull public String getRecordingId();
+ method @IntRange(from=0xffffffff) @NonNull public long getRecordingStartTimeMillis();
+ method @Nullable public android.net.Uri getRecordingUri();
+ method @NonNull public int getRepeatDays();
+ method @IntRange(from=0) @NonNull public long getScheduledDurationMillis();
+ method @IntRange(from=0) @NonNull public long getScheduledStartTimeMillis();
+ method @NonNull public long getStartPaddingMillis();
+ method public void setDescription(@NonNull String);
+ method public void setName(@NonNull String);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TvRecordingInfo> CREATOR;
+ field public static final int FRIDAY = 32; // 0x20
+ field public static final int MONDAY = 2; // 0x2
+ field public static final int RECORDING_ALL = 3; // 0x3
+ field public static final int RECORDING_IN_PROGRESS = 2; // 0x2
+ field public static final int RECORDING_SCHEDULED = 1; // 0x1
+ field public static final int SATURDAY = 64; // 0x40
+ field public static final int SUNDAY = 1; // 0x1
+ field public static final int THURSDAY = 16; // 0x10
+ field public static final int TUESDAY = 4; // 0x4
+ field public static final int WEDNESDAY = 8; // 0x8
+ }
+
public final class TvTrackInfo implements android.os.Parcelable {
method public int describeContents();
method public int getAudioChannelCount();
@@ -27016,10 +27223,13 @@
ctor public TvView(android.content.Context, android.util.AttributeSet);
ctor public TvView(android.content.Context, android.util.AttributeSet, int);
method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
+ method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public String getSelectedTrack(int);
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
+ method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
method public void reset();
+ method public void selectAudioPresentation(int, int);
method public void selectTrack(int, String);
method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
method public void setCallback(@Nullable android.media.tv.TvView.TvInputCallback);
@@ -27052,6 +27262,8 @@
public abstract static class TvView.TvInputCallback {
ctor public TvView.TvInputCallback();
method public void onAitInfoUpdated(@NonNull String, @NonNull android.media.tv.AitInfo);
+ method public void onAudioPresentationSelected(@NonNull String, int, int);
+ method public void onAudioPresentationsChanged(@NonNull String, @NonNull java.util.List<android.media.AudioPresentation>);
method public void onChannelRetuned(String, android.net.Uri);
method public void onConnectionFailed(String);
method public void onContentAllowed(String);
@@ -27206,8 +27418,11 @@
method @CallSuper public void requestStopRecording(@NonNull String);
method @CallSuper public void requestStreamVolume();
method @CallSuper public void requestTrackInfoList();
+ method @CallSuper public void requestTvRecordingInfo(@NonNull String);
+ method @CallSuper public void requestTvRecordingInfoList(@NonNull int);
method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle);
method @CallSuper public void setMediaViewEnabled(boolean);
+ method @CallSuper public void setTvRecordingInfo(@NonNull String, @NonNull android.media.tv.TvRecordingInfo);
method @CallSuper public void setVideoBounds(@NonNull android.graphics.Rect);
}
@@ -27282,6 +27497,9 @@
method public void onRequestStopRecording(@NonNull String, @NonNull String);
method public void onRequestStreamVolume(@NonNull String);
method public void onRequestTrackInfoList(@NonNull String);
+ method public void onRequestTvRecordingInfo(@NonNull String, @NonNull String);
+ method public void onRequestTvRecordingInfoList(@NonNull String, @NonNull int);
+ method public void onSetTvRecordingInfo(@NonNull String, @NonNull String, @NonNull android.media.tv.TvRecordingInfo);
method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect);
method public void onStateChanged(@NonNull String, int, int);
method public void onTeletextAppStateChanged(@NonNull String, int);
@@ -41421,9 +41639,7 @@
}
public final class CallEndpointException extends java.lang.RuntimeException implements android.os.Parcelable {
- ctor public CallEndpointException(@Nullable String);
ctor public CallEndpointException(@Nullable String, int);
- ctor public CallEndpointException(@Nullable String, int, @Nullable Throwable);
method public int describeContents();
method public int getCode();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -42820,6 +43036,7 @@
field public static final String KEY_PREMIUM_CAPABILITY_SUPPORTED_ON_LTE_BOOL = "premium_capability_supported_on_lte_bool";
field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool";
field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
+ field public static final String KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL = "ratchet_nr_advanced_bandwidth_if_rrc_idle_bool";
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
field public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array";
field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array";
@@ -48625,7 +48842,7 @@
field public float density;
field public int densityDpi;
field public int heightPixels;
- field public float scaledDensity;
+ field @Deprecated public float scaledDensity;
field public int widthPixels;
field public float xdpi;
field public float ydpi;
@@ -49168,6 +49385,8 @@
method public static int complexToDimensionPixelSize(int, android.util.DisplayMetrics);
method public static float complexToFloat(int);
method public static float complexToFraction(int, float, float);
+ method public static float convertDimensionToPixels(int, float, @NonNull android.util.DisplayMetrics);
+ method public static float convertPixelsToDimension(int, float, @NonNull android.util.DisplayMetrics);
method public static float deriveDimension(int, float, @NonNull android.util.DisplayMetrics);
method public int getComplexUnit();
method public float getDimension(android.util.DisplayMetrics);
@@ -49539,6 +49758,7 @@
field public static final int HDR_TYPE_HDR10 = 2; // 0x2
field public static final int HDR_TYPE_HDR10_PLUS = 4; // 0x4
field public static final int HDR_TYPE_HLG = 3; // 0x3
+ field public static final int HDR_TYPE_INVALID = -1; // 0xffffffff
field public static final float INVALID_LUMINANCE = -1.0f;
}
@@ -52150,6 +52370,7 @@
public static interface View.OnLongClickListener {
method public boolean onLongClick(android.view.View);
+ method public default boolean onLongClickUseDefaultHapticFeedback(@NonNull android.view.View);
}
public static interface View.OnScrollChangeListener {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d99e151..a40d97e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -329,6 +329,7 @@
field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
field public static final String SOUND_TRIGGER_RUN_IN_BATTERY_SAVER = "android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER";
+ field public static final String STAGE_HEALTH_CONNECT_REMOTE_DATA = "android.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA";
field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
field public static final String START_CROSS_PROFILE_ACTIVITIES = "android.permission.START_CROSS_PROFILE_ACTIVITIES";
field public static final String START_REVIEW_PERMISSION_DECISIONS = "android.permission.START_REVIEW_PERMISSION_DECISIONS";
@@ -3910,8 +3911,8 @@
public final class UserProperties implements android.os.Parcelable {
method public int describeContents();
- method public boolean getIsCredentialSharableWithParent();
- method public boolean getIsMediaSharedWithParent();
+ method public boolean isCredentialShareableWithParent();
+ method public boolean isMediaSharedWithParent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
}
@@ -4596,8 +4597,7 @@
}
public final class HdmiPortInfo implements android.os.Parcelable {
- ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean);
- ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean, boolean);
+ ctor @Deprecated public HdmiPortInfo(int, int, int, boolean, boolean, boolean);
method public int describeContents();
method public int getAddress();
method public int getId();
@@ -4612,6 +4612,15 @@
field public static final int PORT_OUTPUT = 1; // 0x1
}
+ public static final class HdmiPortInfo.Builder {
+ ctor public HdmiPortInfo.Builder(int, int, int);
+ method @NonNull public android.hardware.hdmi.HdmiPortInfo build();
+ method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setArcSupported(boolean);
+ method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setCecSupported(boolean);
+ method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setEarcSupported(boolean);
+ method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setMhlSupported(boolean);
+ }
+
public abstract class HdmiRecordListener {
ctor public HdmiRecordListener();
method public void onClearTimerRecordingResult(int, int);
@@ -5798,14 +5807,19 @@
public final class DisplayPortAltModeInfo implements android.os.Parcelable {
method public int describeContents();
method public int getCableStatus();
+ method public int getLinkTrainingStatus();
method public int getNumberOfLanes();
method public int getPartnerSinkStatus();
+ method public boolean isHotPlugDetectActive();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.DisplayPortAltModeInfo> CREATOR;
field public static final int DISPLAYPORT_ALT_MODE_STATUS_CAPABLE = 2; // 0x2
field public static final int DISPLAYPORT_ALT_MODE_STATUS_ENABLED = 3; // 0x3
field public static final int DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE = 1; // 0x1
field public static final int DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN = 0; // 0x0
+ field public static final int LINK_TRAINING_STATUS_FAILURE = 2; // 0x2
+ field public static final int LINK_TRAINING_STATUS_SUCCESS = 1; // 0x1
+ field public static final int LINK_TRAINING_STATUS_UNKNOWN = 0; // 0x0
}
public class UsbDeviceConnection {
@@ -6677,9 +6691,10 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleStreamVolume(int);
+ method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleVolumeGroupVolume(int);
method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -6689,6 +6704,9 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
+ method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupMaxVolumeIndex(int);
+ method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupMinVolumeIndex(int);
+ method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupVolumeIndex(int);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method public boolean isAudioServerRunning();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isBluetoothVariableLatencyEnabled();
@@ -6716,12 +6734,13 @@
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setBluetoothVariableLatencyEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setDeviceAsNonDefaultForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public void setVolumeGroupVolumeIndex(int, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean supportsBluetoothVariableLatency();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -7128,6 +7147,7 @@
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAudioAttributes();
method public int getId();
+ method @NonNull public String getName();
method public boolean supportsAudioAttributes(@NonNull android.media.AudioAttributes);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategy> CREATOR;
@@ -8767,6 +8787,7 @@
field public static final int TYPE_DVBC = 4; // 0x4
field public static final int TYPE_DVBS = 5; // 0x5
field public static final int TYPE_DVBT = 6; // 0x6
+ field public static final int TYPE_IPTV = 11; // 0xb
field public static final int TYPE_ISDBS = 7; // 0x7
field public static final int TYPE_ISDBS3 = 8; // 0x8
field public static final int TYPE_ISDBT = 9; // 0x9
@@ -8789,6 +8810,11 @@
method public int getHierarchy();
method public long getInnerFec();
method @NonNull public int[] getInterleaving();
+ method @IntRange(from=0) public int getIptvAverageJitterMillis();
+ method @NonNull public String getIptvContentUrl();
+ method @IntRange(from=0) public long getIptvPacketsLost();
+ method @IntRange(from=0) public long getIptvPacketsReceived();
+ method @IntRange(from=0) public int getIptvWorstJitterMillis();
method public int getIsdbtMode();
method public int getIsdbtPartialReceptionFlag();
method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
@@ -8832,6 +8858,11 @@
field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS = 46; // 0x2e
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL = 42; // 0x2a
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST = 43; // 0x2b
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED = 44; // 0x2c
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS = 45; // 0x2d
field public static final int FRONTEND_STATUS_TYPE_ISDBT_MODE = 37; // 0x25
field public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG = 38; // 0x26
field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
@@ -8877,6 +8908,60 @@
field public static final int FRONTEND_STATUS_READINESS_UNSUPPORTED = 4; // 0x4
}
+ public class IptvFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+ ctor public IptvFrontendSettings(@NonNull byte[], @NonNull byte[], int, int, @NonNull android.media.tv.tuner.frontend.IptvFrontendSettingsFec, int, int, long, @NonNull String);
+ method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettings.Builder builder();
+ method @IntRange(from=0) public long getBitrate();
+ method @NonNull public String getContentUrl();
+ method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
+ method public int getDstPort();
+ method @Nullable public android.media.tv.tuner.frontend.IptvFrontendSettingsFec getFec();
+ method public int getIgmp();
+ method public int getProtocol();
+ method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
+ method public int getSrcPort();
+ method public int getType();
+ field public static final int IGMP_UNDEFINED = 0; // 0x0
+ field public static final int IGMP_V1 = 1; // 0x1
+ field public static final int IGMP_V2 = 2; // 0x2
+ field public static final int IGMP_V3 = 4; // 0x4
+ field public static final int PROTOCOL_RTP = 2; // 0x2
+ field public static final int PROTOCOL_UDP = 1; // 0x1
+ field public static final int PROTOCOL_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class IptvFrontendSettings.Builder {
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings build();
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setBitrate(@IntRange(from=0) long);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setContentUrl(@NonNull String);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setDstIpAddress(@NonNull byte[]);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setDstPort(int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setFec(@Nullable android.media.tv.tuner.frontend.IptvFrontendSettingsFec);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setIgmp(int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setProtocol(int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setSrcIpAddress(@NonNull byte[]);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setSrcPort(int);
+ }
+
+ public class IptvFrontendSettingsFec {
+ ctor public IptvFrontendSettingsFec(int, int, int);
+ method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder builder();
+ method @IntRange(from=0) public int getFecColNum();
+ method @IntRange(from=0) public int getFecRowNum();
+ method public int getFecType();
+ field public static final int FEC_TYPE_COLUMN = 1; // 0x1
+ field public static final int FEC_TYPE_COLUMN_ROW = 4; // 0x4
+ field public static final int FEC_TYPE_ROW = 2; // 0x2
+ field public static final int FEC_TYPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class IptvFrontendSettingsFec.Builder {
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec build();
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecColNum(@IntRange(from=0) int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecRowNum(@IntRange(from=0) int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecType(int);
+ }
+
public class Isdbs3FrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
method public int getCodeRateCapability();
method public int getModulationCapability();
@@ -9638,6 +9723,7 @@
method public boolean tearDownInterfaces();
method public boolean tearDownSoftApInterface(@NonNull String);
method public void unregisterCountryCodeChangedListener(@NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
+ field public static final String EXTRA_SCANNING_PARAM_VENDOR_IES = "android.net.wifi.nl80211.extra.SCANNING_PARAM_VENDOR_IES";
field public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR = "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR";
field public static final int SCAN_TYPE_PNO_SCAN = 1; // 0x1
field public static final int SCAN_TYPE_SINGLE_SCAN = 0; // 0x0
@@ -16575,6 +16661,7 @@
public interface WindowManager extends android.view.ViewManager {
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public android.graphics.Region getCurrentImeTouchRegion();
+ method @NonNull public default java.util.List<android.content.ComponentName> notifyScreenshotListeners(int);
method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback);
method public default void unregisterTaskFpsCallback(@NonNull android.window.TaskFpsCallback);
}
@@ -16604,6 +16691,7 @@
method public void interrupt();
method public void onAccessibilityEvent(@NonNull android.view.accessibility.AccessibilityEvent);
method public void onProxyConnected();
+ method public void setAccessibilityFocusAppearance(int, @ColorInt int);
method public void setInstalledAndEnabledServices(@NonNull java.util.List<android.accessibilityservice.AccessibilityServiceInfo>);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8dc7dc3..5f2f623 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -16,6 +16,7 @@
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
+ field public static final String DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA = "android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA";
field public static final String FORCE_DEVICE_POLICY_MANAGER_LOGS = "android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
@@ -29,6 +30,7 @@
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
+ field public static final String MODIFY_HDR_CONVERSION_MODE = "android.permission.MODIFY_HDR_CONVERSION_MODE";
field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
field public static final String MODIFY_USER_PREFERRED_DISPLAY_MODE = "android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE";
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
@@ -131,6 +133,7 @@
method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int);
method public void holdLock(android.os.IBinder, int);
method public static boolean isHighEndGfx();
+ method public void notifySystemPropertiesChanged();
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
@@ -982,59 +985,6 @@
}
-package android.credentials {
-
- public final class CredentialDescription implements android.os.Parcelable {
- ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
- method public int describeContents();
- method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
- method @NonNull public String getFlattenedRequestString();
- method @NonNull public String getType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
- }
-
- public final class CredentialManager {
- method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.RegisterCredentialDescriptionException>);
- method public void unRegisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.UnregisterCredentialDescriptionException>);
- }
-
- public class RegisterCredentialDescriptionException extends java.lang.Exception {
- ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String);
- ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
- ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
- ctor public RegisterCredentialDescriptionException(@NonNull String);
- field @NonNull public final String errorType;
- }
-
- public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
- ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
- ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.List<android.credentials.CredentialDescription>);
- method public int describeContents();
- method @NonNull public java.util.List<android.credentials.CredentialDescription> getCredentialDescriptions();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
- field public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
- }
-
- public class UnregisterCredentialDescriptionException extends java.lang.Exception {
- ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String);
- ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
- ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
- ctor public UnregisterCredentialDescriptionException(@NonNull String);
- field @NonNull public final String errorType;
- }
-
- public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
- ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
- method public int describeContents();
- method @NonNull public android.credentials.CredentialDescription getCredentialDescription();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
- }
-
-}
-
package android.credentials.ui {
public final class CreateCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable {
@@ -1391,11 +1341,14 @@
method public boolean areUserDisabledHdrTypesAllowed();
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearGlobalUserPreferredDisplayMode();
method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
+ method @NonNull public android.hardware.display.HdrConversionMode getHdrConversionMode();
+ method @NonNull public int[] getSupportedHdrOutputTypes();
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE) public void setHdrConversionMode(@NonNull android.hardware.display.HdrConversionMode);
method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
@@ -1408,6 +1361,19 @@
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
}
+ public final class HdrConversionMode implements android.os.Parcelable {
+ ctor public HdrConversionMode(int, int);
+ ctor public HdrConversionMode(int);
+ method public int describeContents();
+ method public int getConversionMode();
+ method public int getPreferredHdrOutputType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.HdrConversionMode> CREATOR;
+ field public static final int HDR_CONVERSION_FORCE = 3; // 0x3
+ field public static final int HDR_CONVERSION_PASSTHROUGH = 1; // 0x1
+ field public static final int HDR_CONVERSION_SYSTEM = 2; // 0x2
+ }
+
}
package android.hardware.fingerprint {
@@ -1705,7 +1671,7 @@
method @NonNull public java.util.Map<java.lang.Integer,java.lang.Boolean> getSurroundFormats();
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public boolean isCsdEnabled();
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS}) public boolean isFullVolumeDevice();
method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setCsd(float);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c0239e8..3c17a33 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
package android.app;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.DETECT_SCREEN_CAPTURE;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -26,6 +27,7 @@
import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.IntDef;
@@ -1016,6 +1018,7 @@
private ComponentCallbacksController mCallbacksController;
@Nullable private IVoiceInteractionManagerService mVoiceInteractionManagerService;
+ private ScreenCaptureCallbackHandler mScreenCaptureCallbackHandler;
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
@@ -9222,4 +9225,43 @@
}
return mWindow.getOnBackInvokedDispatcher();
}
+
+ /**
+ * Interface for observing screen captures of an {@link Activity}.
+ */
+ public interface ScreenCaptureCallback {
+ /**
+ * Called when one of the monitored activities is captured.
+ * This is not invoked if the activity window
+ * has {@link WindowManager.LayoutParams#FLAG_SECURE} set.
+ */
+ void onScreenCaptured();
+ }
+
+ /**
+ * Registers a screen capture callback for this activity.
+ * The callback will be triggered when a screen capture of this activity is attempted.
+ * This callback will be executed on the thread of the passed {@code executor}.
+ * For details, see {@link ScreenCaptureCallback#onScreenCaptured}.
+ */
+ @RequiresPermission(DETECT_SCREEN_CAPTURE)
+ public void registerScreenCaptureCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ScreenCaptureCallback callback) {
+ if (mScreenCaptureCallbackHandler == null) {
+ mScreenCaptureCallbackHandler = new ScreenCaptureCallbackHandler(mToken);
+ }
+ mScreenCaptureCallbackHandler.registerScreenCaptureCallback(executor, callback);
+ }
+
+
+ /**
+ * Unregisters a screen capture callback for this surface.
+ */
+ @RequiresPermission(DETECT_SCREEN_CAPTURE)
+ public void unregisterScreenCaptureCallback(@NonNull ScreenCaptureCallback callback) {
+ if (mScreenCaptureCallbackHandler != null) {
+ mScreenCaptureCallbackHandler.unregisterScreenCaptureCallback(callback);
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b626493..c4d6ad7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5346,6 +5346,20 @@
}
/**
+ * Checks if the process represented by the given {@code pid} is frozen.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public boolean isProcessFrozen(int pid) {
+ try {
+ return getService().isProcessFrozen(pid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return The reason code of whether or not the given UID should be exempted from background
* restrictions here.
*
@@ -5367,6 +5381,31 @@
}
/**
+ * Notifies {@link #getRunningAppProcesses app processes} that the system properties
+ * have changed.
+ *
+ * @see SystemProperties#addChangeCallback
+ *
+ * @hide
+ */
+ @TestApi
+ public void notifySystemPropertiesChanged() {
+ // Note: this cannot use {@link ServiceManager#listServices()} to notify all the services,
+ // as that is not available from tests.
+ final var binder = ActivityManager.getService().asBinder();
+ if (binder != null) {
+ var data = Parcel.obtain();
+ try {
+ binder.transact(IBinder.SYSPROPS_TRANSACTION, data, null /* reply */,
+ 0 /* flags */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ data.recycle();
+ }
+ }
+
+ /**
* A subset of immutable pending intent information suitable for caching on the client side.
*
* @hide
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 57214e0..2a390a7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1997,10 +1997,12 @@
/**
* Sets background activity launch logic won't use pending intent creator foreground state.
+ *
* @hide
*/
- public void setIgnorePendingIntentCreatorForegroundState(boolean state) {
+ public ActivityOptions setIgnorePendingIntentCreatorForegroundState(boolean state) {
mIgnorePendingIntentCreatorForegroundState = state;
+ return this;
}
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 84320ca..25b395b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1380,16 +1380,16 @@
AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_APP_STANDBY;
/**
- * Prevent an app from being placed into forced app standby.
- * {@link ActivityManager#isBackgroundRestricted()}
- * {@link #OP_RUN_ANY_IN_BACKGROUND}
+ * Prevent an app from dismissible notifications. Starting from Android U, notifications with
+ * the ongoing parameter can be dismissed by a user on an unlocked device. An app with
+ * this appop will be exempt and cannot be dismissed by a user.
*
* Only to be used by the system.
*
* @hide
*/
- public static final int OP_SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY =
- AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY;
+ public static final int OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS =
+ AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
/**
* An app op for reading/writing health connect data.
@@ -1408,14 +1408,16 @@
AppProtoEnums.APP_OP_FOREGROUND_SERVICE_SPECIAL_USE;
/**
- * Exempt from start foreground service from background restriction.
+ * Exempt an app from all power-related restrictions, including app standby and doze.
+ * In addition, the app will be able to start foreground services from the background, and the
+ * user will not be able to stop foreground services run by the app.
*
* Only to be used by the system.
*
* @hide
*/
- public static final int OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION =
- AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION;
+ public static final int OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS =
+ AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS;
/**
* Exempt from start foreground service from background with while in user permission
@@ -1448,9 +1450,13 @@
public static final int OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
AppProtoEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD;
+ /** @hide Access to wrist temperature sensors. */
+ public static final int OP_BODY_SENSORS_WRIST_TEMPERATURE =
+ AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 132;
+ public static final int _NUM_OP = 133;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1969,16 +1975,17 @@
"android:system_exempt_from_app_standby";
/**
- * Prevent an app from being placed into forced app standby.
- * {@link ActivityManager#isBackgroundRestricted()}
- * {@link #OP_RUN_ANY_IN_BACKGROUND}
+ * Allow an application to create non-dismissible notifications. Starting from Android U,
+ * notifications with the ongoing parameter can be dismissed by a user on an unlocked device
+ * unless the application that created the notification is exempt.
+ * An application with this appop will be made exempt.
*
* Only to be used by the system.
*
* @hide
*/
- public static final String OPSTR_SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY =
- "android:system_exempt_from_forced_app_standby";
+ public static final String OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS =
+ "android:system_exempt_from_dismissible_notifications";
/**
* Start a foreground service with the type "specialUse".
@@ -1989,14 +1996,16 @@
"android:foreground_service_special_use";
/**
- * Exempt from start foreground service from background restriction.
+ * Exempt an app from all power-related restrictions, including app standby and doze.
+ * In addition, the app will be able to start foreground services from the background, and the
+ * user will not be able to stop foreground services run by the app.
*
* Only to be used by the system.
*
* @hide
*/
- public static final String OPSTR_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION =
- "android:system_exempt_from_fgs_bg_start_restriction";
+ public static final String OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS =
+ "android:system_exempt_from_power_restrictions";
/**
* Exempt from start foreground service from background with while in user permission
@@ -2030,6 +2039,10 @@
public static final String OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
"android:capture_consentless_bugreport_on_userdebug_build";
+ /** Access to wrist temperature body sensors. */
+ public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE =
+ "android:body_sensors_wrist_temperature";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2128,6 +2141,7 @@
OP_READ_MEDIA_VISUAL_USER_SELECTED,
OP_FOREGROUND_SERVICE_SPECIAL_USE,
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
+ OP_BODY_SENSORS_WRIST_TEMPERATURE,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2517,17 +2531,17 @@
new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_APP_STANDBY,
OPSTR_SYSTEM_EXEMPT_FROM_APP_STANDBY,
"SYSTEM_EXEMPT_FROM_APP_STANDBY").build(),
- new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY,
- OPSTR_SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY,
- "SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY").build(),
+ new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
+ OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
+ "SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS").build(),
new AppOpInfo.Builder(OP_READ_WRITE_HEALTH_DATA, OPSTR_READ_WRITE_HEALTH_DATA,
"READ_WRITE_HEALTH_DATA").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
new AppOpInfo.Builder(OP_FOREGROUND_SERVICE_SPECIAL_USE,
OPSTR_FOREGROUND_SERVICE_SPECIAL_USE, "FOREGROUND_SERVICE_SPECIAL_USE")
.setPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE).build(),
- new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION,
- OPSTR_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION,
- "SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION").build(),
+ new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS,
+ OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS,
+ "SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS").build(),
new AppOpInfo.Builder(
OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION,
OPSTR_SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION,
@@ -2541,7 +2555,12 @@
OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
"CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD")
.setPermission(Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD)
- .build()
+ .build(),
+ new AppOpInfo.Builder(OP_BODY_SENSORS_WRIST_TEMPERATURE,
+ OPSTR_BODY_SENSORS_WRIST_TEMPERATURE,
+ "BODY_SENSORS_WRIST_TEMPERATURE")
+ .setPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE)
+ .setDefaultMode(AppOpsManager.MODE_ALLOWED).build()
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index c11961e..fc4e2db 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3289,6 +3289,20 @@
* @hide
*/
@Override
+ public boolean removeCrossProfileIntentFilter(IntentFilter filter, int sourceUserId,
+ int targetUserId, int flags) {
+ try {
+ return mPM.removeCrossProfileIntentFilter(filter, mContext.getOpPackageName(),
+ sourceUserId, targetUserId, flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public void clearCrossProfileIntentFilters(int sourceUserId) {
try {
mPM.clearCrossProfileIntentFilters(sourceUserId, mContext.getOpPackageName());
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index c19a865..20d19c1 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -350,6 +350,7 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION),
new RegularPermission(Manifest.permission.BODY_SENSORS),
+ new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE),
new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
}, false)
);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 0866d94..9dc8ce6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -779,6 +779,10 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
boolean isModernBroadcastQueueEnabled();
+ /** Checks if the process represented by the given pid is frozen. */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
+ boolean isProcessFrozen(int pid);
+
/**
* @return The reason code of whether or not the given UID should be exempted from background
* restrictions here.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 461aa3c..e97e711 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -28,6 +28,7 @@
import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
+import android.app.IScreenCaptureObserver;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.ITaskStackListener;
@@ -354,4 +355,23 @@
*/
android.window.BackNavigationInfo startBackNavigation(
in IWindowFocusObserver focusObserver, in BackAnimationAdapter adaptor);
+
+ /**
+ * registers a callback to be invoked when the screen is captured.
+ *
+ * @param observer callback to be registered.
+ * @param activityToken The token for the activity to set the callback to.
+ * @hide
+ */
+ void registerScreenCaptureObserver(IBinder activityToken, IScreenCaptureObserver observer);
+
+ /**
+ * unregisters the screen capture callback which was registered with
+ * {@link #registerScreenCaptureObserver(ScreenCaptureObserver)}.
+ *
+ * @param observer callback to be unregistered.
+ * @param activityToken The token for the activity to unset the callback from.
+ * @hide
+ */
+ void unregisterScreenCaptureObserver(IBinder activityToken, IScreenCaptureObserver observer);
}
diff --git a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl b/core/java/android/app/IScreenCaptureObserver.aidl
similarity index 64%
copy from core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
copy to core/java/android/app/IScreenCaptureObserver.aidl
index b30a12a..1668e5d10 100644
--- a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
+++ b/core/java/android/app/IScreenCaptureObserver.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,16 +12,11 @@
* WITHOUT 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.credentials;
+package android.app;
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IUnregisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+/** {@hide} */
+interface IScreenCaptureObserver {
+ oneway void onScreenCaptured();
+}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 20869e0..6206f31 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -88,6 +88,7 @@
per-file *Task* = file:/services/core/java/com/android/server/wm/OWNERS
per-file Window* = file:/services/core/java/com/android/server/wm/OWNERS
per-file ConfigurationController.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *ScreenCapture* = file:/services/core/java/com/android/server/wm/OWNERS
# TODO(b/174932174): determine the ownership of KeyguardManager.java
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index c58e627..e1ee3e0 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -57,7 +57,6 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -186,6 +185,7 @@
FLAG_IMMUTABLE,
FLAG_MUTABLE,
FLAG_MUTABLE_UNAUDITED,
+ FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
Intent.FILL_IN_ACTION,
Intent.FILL_IN_DATA,
@@ -280,6 +280,21 @@
public static final int FLAG_MUTABLE_UNAUDITED = FLAG_MUTABLE;
/**
+ * Flag indicating that the created PendingIntent with {@link #FLAG_MUTABLE}
+ * is allowed to have an unsafe implicit Intent within. <p>Starting with
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, for apps that
+ * target SDK {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+ * higher, creation of a PendingIntent with {@link #FLAG_MUTABLE} and an
+ * implicit Intent within will throw an {@link IllegalArgumentException}
+ * for security reasons. To bypass this check, use
+ * {@link #FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT} when creating a PendingIntent.
+ * However, it is strongly recommended to not to use this flag and make the
+ * Intent explicit or the PendingIntent immutable, thereby making the Intent
+ * safe.
+ */
+ public static final int FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT = 1<<24;
+
+ /**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
*/
@@ -414,21 +429,21 @@
}
// Whenever creation or retrieval of a mutable implicit PendingIntent occurs:
- // - For apps with target SDK >= U, Log.wtfStack() that it is blocked for security reasons.
- // This will be changed to a throw of an exception on the server side once we finish
- // migrating to safer PendingIntents b/262253127.
- // - Otherwise, warn that it will be blocked from target SDK U.
- if (isNewMutableImplicitPendingIntent(flags, intent)) {
+ // - For apps with target SDK >= U, throw an IllegalArgumentException for
+ // security reasons.
+ // - Otherwise, warn that it will be blocked from target SDK U onwards.
+ if (isNewMutableDisallowedImplicitPendingIntent(flags, intent)) {
if (Compatibility.isChangeEnabled(BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT)) {
String msg = packageName + ": Targeting U+ (version "
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE + " and above) disallows"
+ " creating or retrieving a PendingIntent with FLAG_MUTABLE,"
- + " an implicit Intent within and without FLAG_NO_CREATE for"
+ + " an implicit Intent within and without FLAG_NO_CREATE and"
+ + " FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT for"
+ " security reasons. To retrieve an already existing"
+ " PendingIntent, use FLAG_NO_CREATE, however, to create a"
+ " new PendingIntent with an implicit Intent use"
+ " FLAG_IMMUTABLE.";
- Slog.wtfStack(TAG, msg);
+ throw new IllegalArgumentException(msg);
} else {
String msg = "New mutable implicit PendingIntent: pkg=" + packageName
+ ", action=" + intent.getAction()
@@ -441,11 +456,15 @@
}
/** @hide */
- public static boolean isNewMutableImplicitPendingIntent(int flags, @NonNull Intent intent) {
+ public static boolean isNewMutableDisallowedImplicitPendingIntent(int flags,
+ @NonNull Intent intent) {
boolean isFlagNoCreateSet = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
boolean isImplicit = (intent.getComponent() == null) && (intent.getPackage() == null);
- return !isFlagNoCreateSet && isFlagMutableSet && isImplicit;
+ boolean isFlagAllowUnsafeImplicitIntentSet =
+ (flags & PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT) != 0;
+ return !isFlagNoCreateSet && isFlagMutableSet && isImplicit
+ && !isFlagAllowUnsafeImplicitIntentSet;
}
/**
diff --git a/core/java/android/app/ScreenCaptureCallbackHandler.java b/core/java/android/app/ScreenCaptureCallbackHandler.java
new file mode 100644
index 0000000..997cf51
--- /dev/null
+++ b/core/java/android/app/ScreenCaptureCallbackHandler.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import java.util.concurrent.Executor;
+
+/** Handles screen capture callbacks.
+ * @hide
+ **/
+public class ScreenCaptureCallbackHandler {
+
+ private final IBinder mActivityToken;
+ private final ScreenCaptureObserver mObserver;
+ private final ArrayMap<Activity.ScreenCaptureCallback, ScreenCaptureRegistration>
+ mScreenCaptureRegistrations = new ArrayMap<>();
+
+ public ScreenCaptureCallbackHandler(@NonNull IBinder activityToken) {
+ mActivityToken = activityToken;
+ mObserver = new ScreenCaptureObserver(mScreenCaptureRegistrations);
+ }
+
+ private static class ScreenCaptureRegistration {
+ Executor mExecutor;
+ Activity.ScreenCaptureCallback mCallback;
+
+ ScreenCaptureRegistration(Executor executor, Activity.ScreenCaptureCallback callback) {
+ this.mExecutor = executor;
+ this.mCallback = callback;
+ }
+ }
+
+ private static class ScreenCaptureObserver extends IScreenCaptureObserver.Stub {
+ ArrayMap<Activity.ScreenCaptureCallback, ScreenCaptureRegistration> mRegistrations;
+
+ ScreenCaptureObserver(
+ ArrayMap<Activity.ScreenCaptureCallback, ScreenCaptureRegistration>
+ registrations) {
+ this.mRegistrations = registrations;
+ }
+
+ @Override
+ public void onScreenCaptured() {
+ for (ScreenCaptureRegistration registration : mRegistrations.values()) {
+ registration.mExecutor.execute(
+ () -> {
+ registration.mCallback.onScreenCaptured();
+ });
+ }
+ }
+ }
+
+ /**
+ * Start monitoring for screen captures of the activity, the callback will be triggered whenever
+ * a screen capture is attempted. This callback will be executed on the thread of the passed
+ * {@code executor}. If the window is FLAG_SECURE, the callback will not be triggered.
+ */
+ public void registerScreenCaptureCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Activity.ScreenCaptureCallback callback) {
+ ScreenCaptureRegistration registration =
+ new ScreenCaptureRegistration(executor, callback);
+ synchronized (mScreenCaptureRegistrations) {
+ if (mScreenCaptureRegistrations.containsKey(callback)) {
+ throw new IllegalStateException(
+ "Capture observer already registered with the activity");
+ }
+ mScreenCaptureRegistrations.put(callback, registration);
+ // register with system server only once.
+ if (mScreenCaptureRegistrations.size() == 1) {
+ try {
+ ActivityTaskManager.getService()
+ .registerScreenCaptureObserver(mActivityToken, mObserver);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ /** Stop monitoring for screen captures of the activity */
+ public void unregisterScreenCaptureCallback(@NonNull Activity.ScreenCaptureCallback callback) {
+ synchronized (mScreenCaptureRegistrations) {
+ if (!mScreenCaptureRegistrations.containsKey(callback)) {
+ throw new IllegalStateException(
+ "Capture observer not registered with the activity");
+ }
+ mScreenCaptureRegistrations.remove(callback);
+ // unregister only if no more registrations are left
+ if (mScreenCaptureRegistrations.size() == 0) {
+ try {
+ ActivityTaskManager.getService().unregisterScreenCaptureObserver(mActivityToken,
+ mObserver);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1f5182a..52d1d68 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -510,11 +510,12 @@
return new BinaryTransparencyManager(ctx, service);
}});
+ // InputManager stores its own static instance for historical purposes.
registerService(Context.INPUT_SERVICE, InputManager.class,
- new StaticServiceFetcher<InputManager>() {
+ new ServiceFetcher<InputManager>() {
@Override
- public InputManager createService() {
- return InputManager.getInstance();
+ public InputManager getService(ContextImpl ctx) {
+ return InputManager.getInstance(ctx);
}});
registerService(Context.DISPLAY_SERVICE, DisplayManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 4a329a9..3957732 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -56,10 +56,9 @@
public abstract int getPermissionPolicy(@UserIdInt int userHandle);
/**
- * Caches {@link DevicePolicyManager#canAdminGrantSensorsPermissionsForUser(int)} for the
- * given user.
+ * True if there is an admin on the device who can grant sensor permissions.
*/
- public abstract boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle);
+ public abstract boolean canAdminGrantSensorsPermissions();
/**
* Empty implementation.
@@ -83,7 +82,7 @@
}
@Override
- public boolean canAdminGrantSensorsPermissionsForUser(int userHandle) {
+ public boolean canAdminGrantSensorsPermissions() {
return false;
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7e5523a..6e6d4e3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,12 +16,14 @@
package android.app.admin;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -10042,6 +10044,58 @@
}
/**
+ * Called by a device owner or profile owner of a managed profile to set the credential manager
+ * policy.
+ *
+ * <p>Affects APIs exposed by {@link android.credentials.CredentialManager}.
+ *
+ * <p>A {@link PackagePolicy#PACKAGE_POLICY_ALLOWLIST} policy type will limit the credential
+ * providers that the user can use to the list of packages in the policy.
+ *
+ * <p>A {@link PackagePolicy#PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM} policy type
+ * allows access from the OEM default credential providers and the allowlist of credential
+ * providers.
+ *
+ * <p>A {@link PackagePolicy#PACKAGE_POLICY_BLOCKLIST} policy type will block the credential
+ * providers listed in the policy from being used by the user.
+ *
+ * @param policy the policy to set, setting this value to {@code null} will allow all packages
+ * @throws SecurityException if caller is not a device owner or profile owner of a
+ * managed profile
+ */
+ public void setCredentialManagerPolicy(@Nullable PackagePolicy policy) {
+ throwIfParentInstance("setCredentialManagerPolicy");
+ if (mService != null) {
+ try {
+ mService.setCredentialManagerPolicy(policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by a device owner or profile owner of a managed profile to retrieve the credential
+ * manager policy.
+ *
+ * @throws SecurityException if caller is not a device owner or profile owner of a
+ * managed profile.
+ * @return the current credential manager policy if null then this policy has not been
+ * configured.
+ */
+ public @Nullable PackagePolicy getCredentialManagerPolicy() {
+ throwIfParentInstance("getCredentialManagerPolicy");
+ if (mService != null) {
+ try {
+ return mService.getCredentialManagerPolicy();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by a profile owner of a managed profile to set the packages that are allowed to
* lookup contacts in the managed profile based on caller id information.
* <p>
@@ -11049,6 +11103,7 @@
* @throws SecurityException if the caller is not a profile owner on an organization-owned
* managed profile.
* @throws IllegalStateException if called after the device setup has been completed.
+ * @throws UnsupportedOperationException if the api is not enabled.
* @see ManagedSubscriptionsPolicy
*/
public void setManagedSubscriptionsPolicy(@Nullable ManagedSubscriptionsPolicy policy) {
@@ -13493,11 +13548,14 @@
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
})
@UserProvisioningState
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = UPSIDE_DOWN_CAKE,
+ requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
public int getUserProvisioningState() {
throwIfParentInstance("getUserProvisioningState");
if (mService != null) {
try {
- return mService.getUserProvisioningState();
+ return mService.getUserProvisioningState(mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -15723,7 +15781,7 @@
}
/**
- * Returns true if the caller is running on a device where the admin can grant
+ * Returns true if the caller is running on a device where an admin can grant
* permissions related to device sensors.
* This is a signal that the device is a fully-managed device where personal usage is
* discouraged.
@@ -15731,7 +15789,7 @@
* {@link #setPermissionGrantState(ComponentName, String, String, int)}.
*
* May be called by any app.
- * @return true if the app can grant device sensors-related permissions, false otherwise.
+ * @return true if an admin can grant device sensors-related permissions, false otherwise.
*/
public boolean canAdminGrantSensorsPermissions() {
throwIfParentInstance("canAdminGrantSensorsPermissions");
@@ -15739,7 +15797,7 @@
return false;
}
try {
- return mService.canAdminGrantSensorsPermissionsForUser(myUserId());
+ return mService.canAdminGrantSensorsPermissions();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index aebeaf0..1ed39c5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -336,6 +336,9 @@
PackagePolicy getManagedProfileCallerIdAccessPolicy();
boolean hasManagedProfileCallerIdAccess(int userId, String packageName);
+ void setCredentialManagerPolicy(in PackagePolicy policy);
+ PackagePolicy getCredentialManagerPolicy();
+
void setManagedProfileContactsAccessPolicy(in PackagePolicy policy);
PackagePolicy getManagedProfileContactsAccessPolicy();
boolean hasManagedProfileContactsAccess(int userId, String packageName);
@@ -412,7 +415,7 @@
CharSequence getDeviceOwnerOrganizationName();
CharSequence getOrganizationNameForUser(int userHandle);
- int getUserProvisioningState();
+ int getUserProvisioningState(int userHandle);
void setUserProvisioningState(int state, int userHandle);
void setAffiliationIds(in ComponentName admin, in List<String> ids);
@@ -550,7 +553,7 @@
int getDeviceOwnerType(in ComponentName admin);
void resetDefaultCrossProfileIntentFilters(int userId);
- boolean canAdminGrantSensorsPermissionsForUser(int userId);
+ boolean canAdminGrantSensorsPermissions();
void setUsbDataSignalingEnabled(String callerPackage, boolean enabled);
boolean isUsbDataSignalingEnabled(String callerPackage);
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index a71590b..d0b4fe4 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -47,6 +47,9 @@
* Methods on this class requires the caller to hold and be granted the
* {@link Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE}.
*
+ * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
+ * separately. </p>
+ *
* @hide
*/
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 4d2317a..e56e53a 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -143,7 +143,7 @@
public ComponentName provider;
/**
- * The default height of the widget when added to a host, in px. The widget will get
+ * The default width of the widget when added to a host, in px. The widget will get
* at least this width, and will often be given more, depending on the host.
*
* <p>This field corresponds to the <code>android:minWidth</code> attribute in
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index baa88e4..15f3f34 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -924,14 +924,25 @@
Log.w(LOG_TAG, "dispatchMessage replaced by attachSystemDataTransport");
}
- /** {@hide} */
+ /**
+ * Attach a bidirectional communication stream to be used as a transport channel for
+ * transporting system data between associated devices.
+ *
+ * @param associationId id of the associated device.
+ * @param in Already connected stream of data incoming from remote
+ * associated device.
+ * @param out Already connected stream of data outgoing to remote associated
+ * device.
+ * @throws DeviceNotAssociatedException Thrown if the associationId was not previously
+ * associated with this app.
+ *
+ * @see #buildPermissionTransferUserConsentIntent(int)
+ * @see #startSystemDataTransfer(int, Executor, OutcomeReceiver)
+ * @see #detachSystemDataTransport(int)
+ */
@RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES)
public void attachSystemDataTransport(int associationId, @NonNull InputStream in,
@NonNull OutputStream out) throws DeviceNotAssociatedException {
- if (!FeatureUtils.isPermSyncEnabled()) {
- Log.e(LOG_TAG, "Calling attachSystemDataTransport, but perm sync is disabled.");
- return;
- }
synchronized (mTransports) {
if (mTransports.contains(associationId)) {
detachSystemDataTransport(associationId);
@@ -947,14 +958,19 @@
}
}
- /** {@hide} */
+ /**
+ * Detach the transport channel that's previously attached for the associated device. The system
+ * will stop transferring any system data when this method is called.
+ *
+ * @param associationId id of the associated device.
+ * @throws DeviceNotAssociatedException Thrown if the associationId was not previously
+ * associated with this app.
+ *
+ * @see #attachSystemDataTransport(int, InputStream, OutputStream)
+ */
@RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES)
public void detachSystemDataTransport(int associationId)
throws DeviceNotAssociatedException {
- if (!FeatureUtils.isPermSyncEnabled()) {
- Log.e(LOG_TAG, "Calling detachSystemDataTransport, but perm sync is disabled.");
- return;
- }
synchronized (mTransports) {
final Transport transport = mTransports.get(associationId);
if (transport != null) {
@@ -1044,28 +1060,33 @@
*
* <p>Only the companion app which owns the association can call this method. Otherwise a null
* IntentSender will be returned from this method and an error will be logged.
- * The The app should launch the {@link Activity} in the returned {@code intentSender}
+ * The app should launch the {@link Activity} in the returned {@code intentSender}
* {@link IntentSender} by calling
* {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}.</p>
*
- * <p>The permission transfer doesn't happen immediately after the call or user consented.
- * The app needs to trigger the system data transfer manually by calling
- * {@code #startSystemDataTransfer(int)}, when it confirms the communication channel between
- * the two devices is established.</p>
+ * <p>The permission transfer doesn't happen immediately after the call or when the user
+ * consents. The app needs to call
+ * {@link #attachSystemDataTransport(int, InputStream, OutputStream)} to attach a transport
+ * channel and
+ * {@link #startSystemDataTransfer(int, Executor, OutcomeReceiver)} to trigger the system data
+ * transfer}.</p>
*
* @param associationId The unique {@link AssociationInfo#getId ID} assigned to the association
* of the companion device recorded by CompanionDeviceManager
* @return An {@link IntentSender} that the app should use to launch the UI for
* the user to confirm the system data transfer request.
+ *
+ * @see #attachSystemDataTransport(int, InputStream, OutputStream)
+ * @see #startSystemDataTransfer(int, Executor, OutcomeReceiver)
*/
@UserHandleAware
@Nullable
public IntentSender buildPermissionTransferUserConsentIntent(int associationId)
throws DeviceNotAssociatedException {
if (!FeatureUtils.isPermSyncEnabled()) {
- Log.e(LOG_TAG, "Calling buildPermissionTransferUserConsentIntent,"
- + " but perm sync is disabled.");
- return null;
+ throw new UnsupportedOperationException("Calling"
+ + " buildPermissionTransferUserConsentIntent, but this API is disabled by the"
+ + " system.");
}
try {
PendingIntent pendingIntent = mService.buildPermissionTransferUserConsentIntent(
@@ -1100,8 +1121,8 @@
@UserHandleAware
public void startSystemDataTransfer(int associationId) throws DeviceNotAssociatedException {
if (!FeatureUtils.isPermSyncEnabled()) {
- Log.e(LOG_TAG, "Calling startSystemDataTransfer, but perm sync is disabled.");
- return;
+ throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this API"
+ + " is disabled by the system.");
}
try {
mService.startSystemDataTransfer(mContext.getOpPackageName(), mContext.getUserId(),
@@ -1115,17 +1136,19 @@
/**
* Start system data transfer which has been previously approved by the user.
*
- * <p>Before calling this method, the app needs to make sure there's a communication channel
- * between two devices, and has prompted user consent dialogs built by one of these methods:
- * {@link #buildPermissionTransferUserConsentIntent(int)}.
- * The transfer may fail if the communication channel is disconnected during the transfer.</p>
+ * <p>Before calling this method, the app needs to make sure
+ * {@link #attachSystemDataTransport(int, InputStream, OutputStream) the transport channel is
+ * attached}, and
+ * {@link #buildPermissionTransferUserConsentIntent(int) the user consent dialog has prompted to
+ * the user}.
+ * The transfer will fail if the transport channel is disconnected or
+ * {@link #detachSystemDataTransport(int) detached} during the transfer.</p>
*
* @param associationId The unique {@link AssociationInfo#getId ID} assigned to the Association
* of the companion device recorded by CompanionDeviceManager
* @param executor The executor which will be used to invoke the result callback.
* @param result The callback to notify the app of the result of the system data transfer.
* @throws DeviceNotAssociatedException Exception if the companion device is not associated
- * @hide
*/
@UserHandleAware
public void startSystemDataTransfer(
@@ -1133,6 +1156,10 @@
@NonNull Executor executor,
@NonNull OutcomeReceiver<Void, CompanionException> result)
throws DeviceNotAssociatedException {
+ if (!FeatureUtils.isPermSyncEnabled()) {
+ throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this API"
+ + " is disabled by the system.");
+ }
try {
mService.startSystemDataTransfer(mContext.getOpPackageName(), mContext.getUserId(),
associationId, new SystemDataTransferCallbackProxy(executor, result));
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index 3a7dd8e..01c5fa1 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -216,7 +216,6 @@
* associated device
* @param out already connected stream of data outgoing to remote associated
* device
- * @hide
*/
@RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES)
public final void attachSystemDataTransport(int associationId, @NonNull InputStream in,
@@ -232,7 +231,6 @@
* through {@link #attachSystemDataTransport}.
*
* @param associationId id of the associated device
- * @hide
*/
@RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES)
public final void detachSystemDataTransport(int associationId)
diff --git a/core/java/android/companion/CompanionException.java b/core/java/android/companion/CompanionException.java
index 9a92401..18dc47d 100644
--- a/core/java/android/companion/CompanionException.java
+++ b/core/java/android/companion/CompanionException.java
@@ -21,8 +21,6 @@
/**
* {@code CompanionException} can be thrown during the companion system data
* transfer process.
- *
- * @hide
*/
public class CompanionException extends RuntimeException {
/** @hide */
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 85af877..06a3b1e 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -70,6 +70,23 @@
*/
public static final boolean DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+ /**
+ * DeviceConfig property, within the clipboard namespace, that determines whether VirtualDevices
+ * are allowed to have siloed Clipboards for the apps running on them. If false, then clipboard
+ * access is blocked entirely for apps running on VirtualDevices.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_ALLOW_VIRTUALDEVICE_SILOS =
+ "allow_virtualdevice_silos";
+
+ /**
+ * Default value for the DEVICE_CONFIG_ALLOW_VIRTUALDEVICE_SILOS property.
+ *
+ * @hide
+ */
+ public static final boolean DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS = false;
+
private final Context mContext;
private final Handler mHandler;
private final IClipboard mService;
@@ -133,7 +150,8 @@
clip,
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -162,6 +180,7 @@
mContext.getOpPackageName(),
mContext.getAttributionTag(),
mContext.getUserId(),
+ mContext.getDeviceId(),
sourcePackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -178,7 +197,8 @@
mService.clearPrimaryClip(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -197,7 +217,8 @@
return mService.getPrimaryClip(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -217,7 +238,8 @@
return mService.getPrimaryClipDescription(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -234,7 +256,8 @@
return mService.hasPrimaryClip(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -248,7 +271,8 @@
mPrimaryClipChangedServiceListener,
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -266,7 +290,8 @@
mPrimaryClipChangedServiceListener,
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -306,7 +331,8 @@
return mService.hasClipboardText(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -326,7 +352,8 @@
return mService.getPrimaryClipSource(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5a153ce..e8f0a89 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -339,10 +339,17 @@
final ProviderInfo cpi = mContext.getPackageManager()
.resolveContentProvider(uri.getAuthority(),
PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final Uri userUri = (mSingleUser
+ && !UserHandle.isSameUser(mMyUid, callingUid))
+ ? maybeAddUserId(uri, callingUserId) : uri;
if (cpi.forceUriPermissions
&& mInterface.checkUriPermission(uri,
callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
- != PermissionChecker.PERMISSION_GRANTED) {
+ != PermissionChecker.PERMISSION_GRANTED
+ && getContext().checkUriPermission(userUri, Binder.getCallingPid(),
+ callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
enumCheckUriPermission,
callingUid, uri.getAuthority(), type);
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index 46ece2b..b2216d4 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -26,22 +26,26 @@
* {@hide}
*/
interface IClipboard {
- void setPrimaryClip(in ClipData clip, String callingPackage, String attributionTag, int userId);
- void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, String attributionTag, int userId,
- String sourcePackage);
- void clearPrimaryClip(String callingPackage, String attributionTag, int userId);
- ClipData getPrimaryClip(String pkg, String attributionTag, int userId);
- ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag, int userId);
- boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId);
+ void setPrimaryClip(in ClipData clip, String callingPackage, String attributionTag, int userId,
+ int deviceId);
+ void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, String attributionTag,
+ int userId, int deviceId, String sourcePackage);
+ void clearPrimaryClip(String callingPackage, String attributionTag, int userId, int deviceId);
+ ClipData getPrimaryClip(String pkg, String attributionTag, int userId, int deviceId);
+ ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag,
+ int userId, int deviceId);
+ boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId, int deviceId);
void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
- String callingPackage, String attributionTag, int userId);
+ String callingPackage, String attributionTag, int userId, int deviceId);
void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
- String callingPackage, String attributionTag, int userId);
+ String callingPackage, String attributionTag, int userId, int deviceId);
/**
* Returns true if the clipboard contains text; false otherwise.
*/
- boolean hasClipboardText(String callingPackage, String attributionTag, int userId);
+ boolean hasClipboardText(String callingPackage, String attributionTag, int userId,
+ int deviceId);
- String getPrimaryClipSource(String callingPackage, String attributionTag, int userId);
+ String getPrimaryClipSource(String callingPackage, String attributionTag, int userId,
+ int deviceId);
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e803084..68b0631 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1234,6 +1234,79 @@
@Overridable
public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L;
+ // Compat framework that per-app overrides rely on only supports booleans. That's why we have
+ // multiple OVERRIDE_*_ORIENTATION_* change ids below instead of just one override with
+ // the integer value.
+
+ /**
+ * Enables {@link #SCREEN_ORIENTATION_PORTRAIT}. Unless OVERRIDE_ANY_ORIENTATION
+ * is enabled, this override is used only when no other fixed orientation was specified by the
+ * activity.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L;
+
+ /**
+ * Enables {@link #SCREEN_ORIENTATION_NOSENSOR}. Unless OVERRIDE_ANY_ORIENTATION
+ * is enabled, this override is used only when no other fixed orientation was specified by the
+ * activity.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR = 265451093L;
+
+ /**
+ * Enables {@link #SCREEN_ORIENTATION_REVERSE_LANDSCAPE}. Unless OVERRIDE_ANY_ORIENTATION
+ * is enabled, this override is used only when activity specify landscape orientation.
+ * This can help apps that assume that landscape display orientation corresponds to {@link
+ * android.view.Surface#ROTATION_90}, while on some devices it can be {@link
+ * android.view.Surface#ROTATION_270}.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE = 266124927L;
+
+ /**
+ * When enabled, allows OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE,
+ * OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR and OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
+ * to override any orientation requested by the activity.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long OVERRIDE_ANY_ORIENTATION = 265464455L;
+
+ /**
+ * This override fixes display orientation to landscape natural orientation when a task is
+ * fullscreen. While display rotation is fixed to landscape, the orientation requested by the
+ * activity will be still respected by bounds resolution logic. For instance, if an activity
+ * requests portrait orientation and this override is set, then activity will appear in the
+ * letterbox mode for fixed orientation with the display rotated to the lanscape natural
+ * orientation.
+ *
+ * <p>This override is applicable only when natural orientation of the device is
+ * landscape and display ignores orientation requestes.
+ *
+ * <p>Main use case for this override are camera-using activities that are portrait-only and
+ * assume alignment with natural device orientation. Such activities can automatically be
+ * rotated with com.android.server.wm.DisplayRotationCompatPolicy but not all of them can
+ * handle dynamic rotation and thus can benefit from this override.
+ *
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L;
+
/**
* Compares activity window layout min width/height with require space for multi window to
* determine if it can be put into multi window mode.
@@ -1453,8 +1526,19 @@
* @hide
*/
public boolean isFixedOrientation() {
- return isFixedOrientationLandscape() || isFixedOrientationPortrait()
- || screenOrientation == SCREEN_ORIENTATION_LOCKED;
+ return isFixedOrientation(screenOrientation);
+ }
+
+ /**
+ * Returns true if the passed activity's orientation is fixed.
+ * @hide
+ */
+ public static boolean isFixedOrientation(@ScreenOrientation int orientation) {
+ return orientation == SCREEN_ORIENTATION_LOCKED
+ // Orientation is fixed to natural display orientation
+ || orientation == SCREEN_ORIENTATION_NOSENSOR
+ || isFixedOrientationLandscape(orientation)
+ || isFixedOrientationPortrait(orientation);
}
/**
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index 1e0deff..37b1778 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -92,18 +93,20 @@
data.writeInt(i);
try {
retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+ reply.readException();
+ while (i < N && reply.readInt() != 0) {
+ listElementClass = readVerifyAndAddElement(creator, reply, loader,
+ listElementClass);
+ if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
+ i++;
+ }
} catch (RemoteException e) {
- Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
- return;
+ throw new BadParcelableException(
+ "Failure retrieving array; only received " + i + " of " + N, e);
+ } finally {
+ reply.recycle();
+ data.recycle();
}
- while (i < N && reply.readInt() != 0) {
- listElementClass = readVerifyAndAddElement(creator, reply, loader,
- listElementClass);
- if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
- i++;
- }
- reply.recycle();
- data.recycle();
}
}
@@ -201,22 +204,29 @@
+ Binder.getCallingPid() + ", sender=" + this);
}
- while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
- reply.writeInt(1);
+ try {
+ reply.writeNoException();
+ while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+ reply.writeInt(1);
- final T parcelable = mList.get(i);
- verifySameType(listElementClass, parcelable.getClass());
- writeElement(parcelable, reply, callFlags);
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ writeElement(parcelable, reply, callFlags);
- if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
- i++;
- }
- if (i < N) {
- if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
- reply.writeInt(0);
- } else {
- if (DEBUG) Log.d(TAG, "Transfer complete, clearing mList reference");
+ if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+ i++;
+ }
+ if (i < N) {
+ if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
+ reply.writeInt(0);
+ } else {
+ if (DEBUG) Log.d(TAG, "Transfer done, clearing mList reference");
+ mList = null;
+ }
+ } catch (RuntimeException e) {
+ if (DEBUG) Log.d(TAG, "Transfer failed, clearing mList reference");
mList = null;
+ throw e;
}
return true;
}
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 66c6c81..d6951ee 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -15,6 +15,7 @@
*/
package android.content.pm;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
@@ -23,6 +24,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.app.Activity;
import android.app.AppOpsManager.Mode;
import android.app.admin.DevicePolicyManager;
@@ -32,6 +34,7 @@
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -167,7 +170,7 @@
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.INTERACT_ACROSS_PROFILES,
- android.Manifest.permission.INTERACT_ACROSS_USERS})
+ INTERACT_ACROSS_USERS})
public void startActivity(
@NonNull Intent intent,
@NonNull UserHandle targetUser,
@@ -196,7 +199,7 @@
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.INTERACT_ACROSS_PROFILES,
- android.Manifest.permission.INTERACT_ACROSS_USERS})
+ INTERACT_ACROSS_USERS})
public void startActivity(
@NonNull Intent intent,
@NonNull UserHandle targetUser,
@@ -500,10 +503,13 @@
*/
@RequiresPermission(
allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
- android.Manifest.permission.INTERACT_ACROSS_USERS})
+ INTERACT_ACROSS_USERS})
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) {
try {
- mService.setInteractAcrossProfilesAppOp(packageName, newMode);
+ mService.setInteractAcrossProfilesAppOp(mContext.getUserId(), packageName, newMode);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -519,9 +525,12 @@
* @hide
*/
@TestApi
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) {
try {
- return mService.canConfigureInteractAcrossProfiles(packageName);
+ return mService.canConfigureInteractAcrossProfiles(mContext.getUserId(), packageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -540,9 +549,13 @@
*
* @hide
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
try {
- return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName);
+ return mService.canUserAttemptToConfigureInteractAcrossProfiles(
+ mContext.getUserId(), packageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -569,7 +582,10 @@
*/
@RequiresPermission(
allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
- android.Manifest.permission.INTERACT_ACROSS_USERS})
+ INTERACT_ACROSS_USERS})
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
public void resetInteractAcrossProfilesAppOps(
@NonNull Collection<String> previousCrossProfilePackages,
@NonNull Set<String> newCrossProfilePackages) {
@@ -584,7 +600,8 @@
return;
}
try {
- mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages);
+ mService.resetInteractAcrossProfilesAppOps(
+ mContext.getUserId(), unsetCrossProfilePackages);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -609,10 +626,13 @@
*/
@RequiresPermission(
allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
- android.Manifest.permission.INTERACT_ACROSS_USERS})
+ INTERACT_ACROSS_USERS})
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
public void clearInteractAcrossProfilesAppOps() {
try {
- mService.clearInteractAcrossProfilesAppOps();
+ mService.clearInteractAcrossProfilesAppOps(mContext.getUserId());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 4f2c106..716ce30 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -36,9 +36,9 @@
List<UserHandle> getTargetUserProfiles(in String callingPackage);
boolean canInteractAcrossProfiles(in String callingPackage);
boolean canRequestInteractAcrossProfiles(in String callingPackage);
- void setInteractAcrossProfilesAppOp(in String packageName, int newMode);
- boolean canConfigureInteractAcrossProfiles(in String packageName);
- boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName);
- void resetInteractAcrossProfilesAppOps(in List<String> packageNames);
- void clearInteractAcrossProfilesAppOps();
+ void setInteractAcrossProfilesAppOp(int userId, in String packageName, int newMode);
+ boolean canConfigureInteractAcrossProfiles(int userId, in String packageName);
+ boolean canUserAttemptToConfigureInteractAcrossProfiles(int userId, in String packageName);
+ void resetInteractAcrossProfilesAppOps(int userId, in List<String> packageNames);
+ void clearInteractAcrossProfilesAppOps(int userId);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3430a61..dfaa065 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -281,6 +281,9 @@
void addCrossProfileIntentFilter(in IntentFilter intentFilter, String ownerPackage,
int sourceUserId, int targetUserId, int flags);
+ boolean removeCrossProfileIntentFilter(in IntentFilter intentFilter, String ownerPackage,
+ int sourceUserId, int targetUserId, int flags);
+
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
String[] setDistractingPackageRestrictionsAsUser(in String[] packageNames, int restrictionFlags,
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1a6f6b8..dc8c0ab 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2400,6 +2400,14 @@
public static final int DELETE_FAILED_APP_PINNED = -7;
/**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} if the system failed to delete the package
+ * for any child profile with {@link UserProperties#getDeleteAppWithParent()} as true.
+ * @hide
+ */
+ public static final int DELETE_FAILED_FOR_CHILD_PROFILE = -8;
+
+ /**
* Return code that is passed to the {@link IPackageMoveObserver} when the
* package has been successfully moved by the system.
*
@@ -3474,6 +3482,18 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports Telephony APIs for Satellite communication.
+ *
+ * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY_MESSAGING}
+ * has been defined.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_SATELLITE = "android.hardware.telephony.satellite";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device supports Telephony APIs for the subscription.
*
* <p>This feature should only be defined if {@link #FEATURE_TELEPHONY} has been defined.
@@ -9613,6 +9633,23 @@
@UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags);
/**
+ * Removes all {@code CrossProfileIntentFilter}s which matches the specified intent filer,
+ * source, target and flag.
+ *
+ * @param filter The {@link IntentFilter} the intent has to match
+ * @param sourceUserId The source user id.
+ * @param targetUserId The target user id.
+ * @param flags The possible values are {@link #SKIP_CURRENT_PROFILE} and
+ * {@link #ONLY_IF_NO_MATCH_FOUND}.
+ * @hide
+ */
+ public boolean removeCrossProfileIntentFilter(@NonNull IntentFilter filter,
+ @UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags) {
+ throw new UnsupportedOperationException(
+ "removeCrossProfileIntentFilter not implemented in subclass");
+ }
+
+ /**
* Clearing {@code CrossProfileIntentFilter}s which have the specified user
* as their source, and have been set by the app calling this method.
*
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 6ca708a..f3209f9 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -323,6 +323,7 @@
* permissions:
* {@link android.Manifest.permission#ACTIVITY_RECOGNITION},
* {@link android.Manifest.permission#BODY_SENSORS},
+ * {@link android.Manifest.permission#BODY_SENSORS_WRIST_TEMPERATURE},
* {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}.
*/
@RequiresPermission(
@@ -332,6 +333,7 @@
anyOf = {
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.BODY_SENSORS,
+ Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE,
Manifest.permission.HIGH_SAMPLING_RATE_SENSORS,
}
)
@@ -388,7 +390,7 @@
*
* <ul>
* <li>
- * The type has a 1 minute timeout.
+ * The type has a 3 minute timeout.
* A foreground service of this type must be stopped within the timeout by
* {@link android.app.Service#stopSelf),
* or {@link android.content.Context#stopService).
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index d75205c..77b1954 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -59,8 +59,9 @@
"crossProfileIntentResolutionStrategy";
private static final String ATTR_MEDIA_SHARED_WITH_PARENT =
"mediaSharedWithParent";
- private static final String ATTR_CREDENTIAL_SHARABLE_WITH_PARENT =
- "credentialSharableWithParent";
+ private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
+ "credentialShareableWithParent";
+ private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
@@ -73,7 +74,8 @@
INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL,
INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
INDEX_MEDIA_SHARED_WITH_PARENT,
- INDEX_CREDENTIAL_SHARABLE_WITH_PARENT
+ INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
+ INDEX_DELETE_APP_WITH_PARENT,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -87,7 +89,8 @@
private static final int INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL = 6;
private static final int INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY = 7;
private static final int INDEX_MEDIA_SHARED_WITH_PARENT = 8;
- private static final int INDEX_CREDENTIAL_SHARABLE_WITH_PARENT = 9;
+ private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
+ private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -312,6 +315,7 @@
setCrossProfileIntentFilterAccessControl(
orig.getCrossProfileIntentFilterAccessControl());
setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy());
+ setDeleteAppWithParent(orig.getDeleteAppWithParent());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -323,8 +327,8 @@
}
// Add items that have no permission requirements at all.
setShowInLauncher(orig.getShowInLauncher());
- setIsMediaSharedWithParent(orig.getIsMediaSharedWithParent());
- setIsCredentialSharableWithParent(orig.getIsCredentialSharableWithParent());
+ setMediaSharedWithParent(orig.isMediaSharedWithParent());
+ setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
}
/**
@@ -418,6 +422,24 @@
private boolean mStartWithParent;
/**
+ * Returns whether an app in the profile should be deleted when the same package in
+ * the parent user is being deleted.
+ * This only applies for users that have parents (i.e. for profiles).
+ * @hide
+ */
+ public boolean getDeleteAppWithParent() {
+ if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) return mDeleteAppWithParent;
+ if (mDefaultProperties != null) return mDefaultProperties.mDeleteAppWithParent;
+ throw new SecurityException("You don't have permission to query deleteAppWithParent");
+ }
+ /** @hide */
+ public void setDeleteAppWithParent(boolean val) {
+ this.mDeleteAppWithParent = val;
+ setPresent(INDEX_DELETE_APP_WITH_PARENT);
+ }
+ private boolean mDeleteAppWithParent;
+
+ /**
* Return whether, and how, select user restrictions or device policies should be inherited
* from other user.
*
@@ -496,13 +518,13 @@
* Returns whether a profile shares media with its parent user.
* This only applies for users that have parents (i.e. for profiles).
*/
- public boolean getIsMediaSharedWithParent() {
+ public boolean isMediaSharedWithParent() {
if (isPresent(INDEX_MEDIA_SHARED_WITH_PARENT)) return mMediaSharedWithParent;
if (mDefaultProperties != null) return mDefaultProperties.mMediaSharedWithParent;
- throw new SecurityException("You don't have permission to query isMediaSharedWithParent");
+ throw new SecurityException("You don't have permission to query mediaSharedWithParent");
}
/** @hide */
- public void setIsMediaSharedWithParent(boolean val) {
+ public void setMediaSharedWithParent(boolean val) {
this.mMediaSharedWithParent = val;
setPresent(INDEX_MEDIA_SHARED_WITH_PARENT);
}
@@ -512,18 +534,20 @@
* Returns whether a profile can have shared lockscreen credential with its parent user.
* This only applies for users that have parents (i.e. for profiles).
*/
- public boolean getIsCredentialSharableWithParent() {
- if (isPresent(INDEX_CREDENTIAL_SHARABLE_WITH_PARENT)) return mCredentialSharableWithParent;
- if (mDefaultProperties != null) return mDefaultProperties.mCredentialSharableWithParent;
+ public boolean isCredentialShareableWithParent() {
+ if (isPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT)) {
+ return mCredentialShareableWithParent;
+ }
+ if (mDefaultProperties != null) return mDefaultProperties.mCredentialShareableWithParent;
throw new SecurityException(
- "You don't have permission to query isCredentialSharableWithParent");
+ "You don't have permission to query credentialShareableWithParent");
}
/** @hide */
- public void setIsCredentialSharableWithParent(boolean val) {
- this.mCredentialSharableWithParent = val;
- setPresent(INDEX_CREDENTIAL_SHARABLE_WITH_PARENT);
+ public void setCredentialShareableWithParent(boolean val) {
+ this.mCredentialShareableWithParent = val;
+ setPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT);
}
- private boolean mCredentialSharableWithParent;
+ private boolean mCredentialShareableWithParent;
/*
Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during
@@ -605,8 +629,9 @@
+ getCrossProfileIntentFilterAccessControl()
+ ", mCrossProfileIntentResolutionStrategy="
+ getCrossProfileIntentResolutionStrategy()
- + ", mMediaSharedWithParent=" + getIsMediaSharedWithParent()
- + ", mCredentialSharableWithParent=" + getIsCredentialSharableWithParent()
+ + ", mMediaSharedWithParent=" + isMediaSharedWithParent()
+ + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
+ + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
+ "}";
}
@@ -629,9 +654,10 @@
+ getCrossProfileIntentFilterAccessControl());
pw.println(prefix + " mCrossProfileIntentResolutionStrategy="
+ getCrossProfileIntentResolutionStrategy());
- pw.println(prefix + " mMediaSharedWithParent=" + getIsMediaSharedWithParent());
- pw.println(prefix + " mCredentialSharableWithParent="
- + getIsCredentialSharableWithParent());
+ pw.println(prefix + " mMediaSharedWithParent=" + isMediaSharedWithParent());
+ pw.println(prefix + " mCredentialShareableWithParent="
+ + isCredentialShareableWithParent());
+ pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent());
}
/**
@@ -690,10 +716,13 @@
setCrossProfileIntentResolutionStrategy(parser.getAttributeInt(i));
break;
case ATTR_MEDIA_SHARED_WITH_PARENT:
- setIsMediaSharedWithParent(parser.getAttributeBoolean(i));
+ setMediaSharedWithParent(parser.getAttributeBoolean(i));
break;
- case ATTR_CREDENTIAL_SHARABLE_WITH_PARENT:
- setIsCredentialSharableWithParent(parser.getAttributeBoolean(i));
+ case ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT:
+ setCredentialShareableWithParent(parser.getAttributeBoolean(i));
+ break;
+ case ATTR_DELETE_APP_WITH_PARENT:
+ setDeleteAppWithParent(parser.getAttributeBoolean(i));
break;
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
@@ -746,9 +775,13 @@
serializer.attributeBoolean(null, ATTR_MEDIA_SHARED_WITH_PARENT,
mMediaSharedWithParent);
}
- if (isPresent(INDEX_CREDENTIAL_SHARABLE_WITH_PARENT)) {
- serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHARABLE_WITH_PARENT,
- mCredentialSharableWithParent);
+ if (isPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT)) {
+ serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT,
+ mCredentialShareableWithParent);
+ }
+ if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) {
+ serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT,
+ mDeleteAppWithParent);
}
}
@@ -765,7 +798,8 @@
dest.writeInt(mCrossProfileIntentFilterAccessControl);
dest.writeInt(mCrossProfileIntentResolutionStrategy);
dest.writeBoolean(mMediaSharedWithParent);
- dest.writeBoolean(mCredentialSharableWithParent);
+ dest.writeBoolean(mCredentialShareableWithParent);
+ dest.writeBoolean(mDeleteAppWithParent);
}
/**
@@ -785,7 +819,8 @@
mCrossProfileIntentFilterAccessControl = source.readInt();
mCrossProfileIntentResolutionStrategy = source.readInt();
mMediaSharedWithParent = source.readBoolean();
- mCredentialSharableWithParent = source.readBoolean();
+ mCredentialShareableWithParent = source.readBoolean();
+ mDeleteAppWithParent = source.readBoolean();
}
@Override
@@ -822,7 +857,8 @@
private @CrossProfileIntentResolutionStrategy int mCrossProfileIntentResolutionStrategy =
CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT;
private boolean mMediaSharedWithParent = false;
- private boolean mCredentialSharableWithParent = false;
+ private boolean mCredentialShareableWithParent = false;
+ private boolean mDeleteAppWithParent = false;
public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
mShowInLauncher = showInLauncher;
@@ -874,13 +910,19 @@
return this;
}
- public Builder setIsMediaSharedWithParent(boolean mediaSharedWithParent) {
+ public Builder setMediaSharedWithParent(boolean mediaSharedWithParent) {
mMediaSharedWithParent = mediaSharedWithParent;
return this;
}
- public Builder setIsCredentialSharableWithParent(boolean credentialSharableWithParent) {
- mCredentialSharableWithParent = credentialSharableWithParent;
+ public Builder setCredentialShareableWithParent(boolean credentialShareableWithParent) {
+ mCredentialShareableWithParent = credentialShareableWithParent;
+ return this;
+ }
+
+ /** Sets the value for {@link #mDeleteAppWithParent}*/
+ public Builder setDeleteAppWithParent(boolean deleteAppWithParent) {
+ mDeleteAppWithParent = deleteAppWithParent;
return this;
}
@@ -896,7 +938,8 @@
mCrossProfileIntentFilterAccessControl,
mCrossProfileIntentResolutionStrategy,
mMediaSharedWithParent,
- mCredentialSharableWithParent);
+ mCredentialShareableWithParent,
+ mDeleteAppWithParent);
}
} // end Builder
@@ -910,8 +953,8 @@
@CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
@CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
boolean mediaSharedWithParent,
- boolean credentialSharableWithParent) {
-
+ boolean credentialShareableWithParent,
+ boolean deleteAppWithParent) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
@@ -921,7 +964,8 @@
setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA);
setCrossProfileIntentFilterAccessControl(crossProfileIntentFilterAccessControl);
setCrossProfileIntentResolutionStrategy(crossProfileIntentResolutionStrategy);
- setIsMediaSharedWithParent(mediaSharedWithParent);
- setIsCredentialSharableWithParent(credentialSharableWithParent);
+ setMediaSharedWithParent(mediaSharedWithParent);
+ setCredentialShareableWithParent(credentialShareableWithParent);
+ setDeleteAppWithParent(deleteAppWithParent);
}
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 0def59f..335975b 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -98,6 +98,14 @@
/**
* Current user preference for the scaling factor for fonts, relative
* to the base density scaling.
+ *
+ * <p>Note: Please do not use this to hardcode font size equations. The equation for font
+ * scaling is now non-linear; this coefficient is no longer used as a direct multiplier to
+ * determine font size. It exists for informational purposes only.
+ *
+ * <p>Please use {@link android.util.TypedValue#applyDimension(int, float, DisplayMetrics)} or
+ * {@link android.util.TypedValue#deriveDimension(int, float, DisplayMetrics)} to convert
+ * between scaled font size dimensions and pixels.
*/
public float fontScale;
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index ec6a396..c67d37e 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -17,7 +17,6 @@
package android.credentials;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.credentials.CredentialEntry;
@@ -31,11 +30,11 @@
/**
* Represents the type and contained data fields of a {@link Credential}.
- * @hide
*/
-@TestApi
public final class CredentialDescription implements Parcelable {
+ private static final int MAX_ALLOWED_ENTRIES_PER_DESCRIPTION = 16;
+
/**
* The credential type.
*/
@@ -59,8 +58,11 @@
*
* @param type the type of the credential returned.
* @param flattenedRequestString flattened JSON string that will be matched with requests.
- * @param credentialEntries a list of {@link CredentialEntry}s that have been returned
- * to the developer upon credential creation.
+ * @param credentialEntries a list of {@link CredentialEntry}s that are to be shown on the
+ * account selector if a credential matches with this description.
+ * Each entry contains information to be displayed within an
+ * entry on the UI, as well as a {@link android.app.PendingIntent}
+ * that will be invoked if the user selects this entry.
*
* @throws IllegalArgumentException If type is empty.
*/
@@ -70,6 +72,11 @@
mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
mFlattenedRequestString = Preconditions.checkStringNotEmpty(flattenedRequestString);
mCredentialEntries = Objects.requireNonNull(credentialEntries);
+ Preconditions.checkArgument(credentialEntries.size()
+ <= MAX_ALLOWED_ENTRIES_PER_DESCRIPTION,
+ "The number of Credential Entries exceed 16.");
+ Preconditions.checkArgument(compareEntryTypes(type, credentialEntries) == 0,
+ "Credential Entry type(s) do not match the request type.");
}
private CredentialDescription(@NonNull Parcel in) {
@@ -88,6 +95,14 @@
mCredentialEntries);
}
+ private static int compareEntryTypes(@NonNull String type,
+ @NonNull List<CredentialEntry> credentialEntries) {
+ return credentialEntries.stream()
+ .filter(credentialEntry ->
+ !credentialEntry.getType().equals(type)).toList().size();
+
+ }
+
public static final @NonNull Parcelable.Creator<CredentialDescription> CREATOR =
new Parcelable.Creator<CredentialDescription>() {
@Override
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 232d063..1db14a3 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
@@ -314,53 +313,37 @@
}
/**
- * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
- * a CredentialProvider has. This registry will then be used by
- * {@link #executeGetCredential(GetCredentialRequest, Activity,
- * CancellationSignal, Executor, OutcomeReceiver)} to determine where to
- * fetch the requested {@link Credential} from.
- *
+ * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
+ * a CredentialProvider has. This registry will then be used to determine where to
+ * fetch the requested {@link Credential} from. Not all credential types will be supported.
+ * The distinction will be made by the JetPack layer. For the types that are supported,
+ * JetPack will add a new key-value pair into {@link GetCredentialRequest}. These will not
+ * be persistent on the device. The Credential Providers will need to call this API again
+ * upon device reboot.
*
* @param request the request data
- * @param cancellationSignal an optional signal that allows for cancelling this call
- * @param executor the callback will take place on this {@link Executor}
- * @param callback the callback invoked when the request succeeds or fails
*
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
+ * @throws {@link com.android.server.credentials.NonCredentialProviderCallerException}
+ * if the calling package name is not also listed as a Credential Provider.
+ * @throws {@link IllegalArgumentException} if the calling Credential Provider can not handle
+ * one or more of the Credential Types that are sent for registration.
*
- * @hide
*/
- @TestApi
public void registerCredentialDescription(
- @NonNull RegisterCredentialDescriptionRequest request,
- @Nullable CancellationSignal cancellationSignal,
- @CallbackExecutor @NonNull Executor executor,
- @NonNull OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {
+ @NonNull RegisterCredentialDescriptionRequest request) {
if (!isCredentialDescriptionApiEnabled()) {
throw new UnsupportedOperationException("This API is not currently supported.");
}
- requireNonNull(executor, "executor must not be null");
- requireNonNull(callback, "callback must not be null");
+ requireNonNull(request, "request must not be null");
- if (cancellationSignal != null && cancellationSignal.isCanceled()) {
- Log.w(TAG, "executeCreateCredential already canceled");
- return;
- }
-
- ICancellationSignal cancelRemote = null;
try {
- cancelRemote = mService.registerCredentialDescription(request,
- new RegisterCredentialDescriptionTransport(executor, callback),
- mContext.getOpPackageName());
+ mService.registerCredentialDescription(request, mContext.getOpPackageName());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
-
- if (cancellationSignal != null && cancelRemote != null) {
- cancellationSignal.setRemote(cancelRemote);
- }
}
@@ -370,45 +353,25 @@
*
*
* @param request the request data
- * @param cancellationSignal an optional signal that allows for cancelling this call
- * @param executor the callback will take place on this {@link Executor}
- * @param callback the callback invoked when the request succeeds or fails
*
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
*
- * @hide
*/
- @TestApi
- public void unRegisterCredentialDescription(
- @NonNull UnregisterCredentialDescriptionRequest request,
- @Nullable CancellationSignal cancellationSignal,
- @CallbackExecutor @NonNull Executor executor,
- @NonNull OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {
+ public void unregisterCredentialDescription(
+ @NonNull UnregisterCredentialDescriptionRequest request) {
if (!isCredentialDescriptionApiEnabled()) {
throw new UnsupportedOperationException("This API is not currently supported.");
}
- requireNonNull(executor, "executor must not be null");
- requireNonNull(callback, "callback must not be null");
+ requireNonNull(request, "request must not be null");
- if (cancellationSignal != null && cancellationSignal.isCanceled()) {
- Log.w(TAG, "executeCreateCredential already canceled");
- return;
- }
-
- ICancellationSignal cancelRemote = null;
try {
- cancelRemote = mService.unRegisterCredentialDescription(request,
- new UnregisterCredentialDescriptionTransport(executor, callback),
- mContext.getOpPackageName());
+ mService.unregisterCredentialDescription(request, mContext.getOpPackageName());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
- if (cancellationSignal != null && cancelRemote != null) {
- cancellationSignal.setRemote(cancelRemote);
- }
}
private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
@@ -572,54 +535,4 @@
() -> mCallback.onError(new SetEnabledProvidersException(errorType, message)));
}
}
-
- private static class RegisterCredentialDescriptionTransport
- extends IRegisterCredentialDescriptionCallback.Stub {
-
- private final Executor mExecutor;
- private final OutcomeReceiver<Void, RegisterCredentialDescriptionException> mCallback;
-
- private RegisterCredentialDescriptionTransport(Executor executor,
- OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void onResponse() {
- mCallback.onResult(null);
- }
-
- @Override
- public void onError(String errorCode, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new RegisterCredentialDescriptionException(errorCode,
- message)));
- }
- }
-
- private static class UnregisterCredentialDescriptionTransport
- extends IUnregisterCredentialDescriptionCallback.Stub {
-
- private final Executor mExecutor;
- private final OutcomeReceiver<Void, UnregisterCredentialDescriptionException> mCallback;
-
- private UnregisterCredentialDescriptionTransport(Executor executor,
- OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void onResponse() {
- mCallback.onResult(null);
- }
-
- @Override
- public void onError(String errorCode, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new UnregisterCredentialDescriptionException(errorCode,
- message)));
- }
- }
}
diff --git a/core/java/android/credentials/GetCredentialOption.java b/core/java/android/credentials/GetCredentialOption.java
index 55daf86..f2895c7 100644
--- a/core/java/android/credentials/GetCredentialOption.java
+++ b/core/java/android/credentials/GetCredentialOption.java
@@ -32,6 +32,14 @@
public final class GetCredentialOption implements Parcelable {
/**
+ * Bundle key to the flattened version of the JSON request string. Framework will use this key
+ * to determine which types of Credentials will utilize Credential Registry when filtering
+ * Credential Providers to ping.
+ */
+ public static final String FLATTENED_REQUEST = "android.credentials"
+ + ".GetCredentialOption.FLATTENED_REQUEST_STRING";
+
+ /**
* The requested credential type.
*/
@NonNull
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index 75b3d0c..4ca3124 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -27,8 +27,6 @@
import android.credentials.ICreateCredentialCallback;
import android.credentials.IGetCredentialCallback;
import android.credentials.IListEnabledProvidersCallback;
-import android.credentials.IRegisterCredentialDescriptionCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
import android.credentials.ISetEnabledProvidersCallback;
import android.os.ICancellationSignal;
@@ -49,8 +47,8 @@
void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
- @nullable ICancellationSignal registerCredentialDescription(in RegisterCredentialDescriptionRequest request, in IRegisterCredentialDescriptionCallback callback, String callingPackage);
+ void registerCredentialDescription(in RegisterCredentialDescriptionRequest request, String callingPackage);
- @nullable ICancellationSignal unRegisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, in IUnregisterCredentialDescriptionCallback callback, String callingPackage);
+ void unregisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, String callingPackage);
}
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionException.java b/core/java/android/credentials/RegisterCredentialDescriptionException.java
deleted file mode 100644
index d19bb8e..0000000
--- a/core/java/android/credentials/RegisterCredentialDescriptionException.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.os.CancellationSignal;
-import android.os.OutcomeReceiver;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Represents an error encountered during the {@link
- * CredentialManager#registerCredentialDescription(RegisterCredentialDescriptionRequest,
- * CancellationSignal, Executor, OutcomeReceiver)} operation.
- *
- * @hide
- */
-@TestApi
-public class RegisterCredentialDescriptionException extends Exception {
-
- @NonNull public final String errorType;
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable String message) {
- this(errorType, message, null);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(
- @NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
- super(message, cause);
- this.errorType =
- Preconditions
- .checkStringNotEmpty(errorType, "errorType must not be empty");
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable Throwable cause) {
- this(errorType, null, cause);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(@NonNull String errorType) {
- this(errorType, null, null);
- }
-}
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
index f257ac5..b5f3610 100644
--- a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
@@ -19,7 +19,6 @@
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,20 +27,18 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* A request to register a {@link ComponentName} that contains an actively provisioned
* {@link Credential} represented by a {@link CredentialDescription}.
*
- * @hide
*/
-@TestApi
public final class RegisterCredentialDescriptionRequest implements Parcelable {
- public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
-
@NonNull
private final List<CredentialDescription> mCredentialDescriptions;
@@ -51,7 +48,7 @@
}
public RegisterCredentialDescriptionRequest(
- @NonNull List<CredentialDescription> credentialDescriptions) {
+ @NonNull Set<CredentialDescription> credentialDescriptions) {
mCredentialDescriptions = new ArrayList<>(requireNonNull(credentialDescriptions));
}
@@ -89,7 +86,7 @@
}
@NonNull
- public List<CredentialDescription> getCredentialDescriptions() {
- return mCredentialDescriptions;
+ public Set<CredentialDescription> getCredentialDescriptions() {
+ return new HashSet<>(mCredentialDescriptions);
}
}
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionException.java b/core/java/android/credentials/UnregisterCredentialDescriptionException.java
deleted file mode 100644
index a16915a..0000000
--- a/core/java/android/credentials/UnregisterCredentialDescriptionException.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.os.CancellationSignal;
-import android.os.OutcomeReceiver;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Represents an error encountered during the {@link
- * CredentialManager#registerCredentialDescription(RegisterCredentialDescriptionRequest,
- * CancellationSignal, Executor, OutcomeReceiver)} operation.
- *
- * @hide
- */
-@TestApi
-public class UnregisterCredentialDescriptionException extends Exception {
-
- @NonNull public final String errorType;
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable String message) {
- this(errorType, message, null);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(
- @NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
- super(message, cause);
- this.errorType =
- Preconditions
- .checkStringNotEmpty(errorType, "errorType must not be empty");
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable Throwable cause) {
- this(errorType, null, cause);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(@NonNull String errorType) {
- this(errorType, null, null);
- }
-}
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
index 6cf40e7..f175ba4 100644
--- a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
@@ -19,38 +19,47 @@
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.AnnotationValidations;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* A request to unregister a {@link ComponentName} that contains an actively provisioned
* {@link Credential} represented by a {@link CredentialDescription}. *
*
- * @hide
*/
-@TestApi
public final class UnregisterCredentialDescriptionRequest implements Parcelable {
@NonNull
- private final CredentialDescription mCredentialDescription;
+ private final List<CredentialDescription> mCredentialDescriptions;
- public UnregisterCredentialDescriptionRequest(@NonNull CredentialDescription
- credentialDescription) {
- mCredentialDescription = requireNonNull(credentialDescription);
+ public UnregisterCredentialDescriptionRequest(
+ @NonNull CredentialDescription credentialDescription) {
+ mCredentialDescriptions = Arrays.asList(requireNonNull(credentialDescription));
+ }
+
+ public UnregisterCredentialDescriptionRequest(
+ @NonNull Set<CredentialDescription> credentialDescriptions) {
+ mCredentialDescriptions = new ArrayList<>(requireNonNull(credentialDescriptions));
}
private UnregisterCredentialDescriptionRequest(@NonNull Parcel in) {
- CredentialDescription credentialDescription =
- CredentialDescription.CREATOR.createFromParcel(in);
+ List<CredentialDescription> credentialDescriptions = new ArrayList<>();
+ in.readTypedList(credentialDescriptions, CredentialDescription.CREATOR);
- mCredentialDescription = credentialDescription;
+ mCredentialDescriptions = new ArrayList<>();
AnnotationValidations.validate(android.annotation.NonNull.class, null,
- credentialDescription);
+ credentialDescriptions);
+ mCredentialDescriptions.addAll(credentialDescriptions);
}
public static final @NonNull Parcelable.Creator<UnregisterCredentialDescriptionRequest>
@@ -73,11 +82,11 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- mCredentialDescription.writeToParcel(dest, flags);
+ dest.writeTypedList(mCredentialDescriptions, flags);
}
@NonNull
- public CredentialDescription getCredentialDescription() {
- return mCredentialDescription;
+ public Set<CredentialDescription> getCredentialDescriptions() {
+ return new HashSet<>(mCredentialDescriptions);
}
}
diff --git a/core/java/android/credentials/ui/BaseDialogResult.java b/core/java/android/credentials/ui/BaseDialogResult.java
index 5223314..f0442de 100644
--- a/core/java/android/credentials/ui/BaseDialogResult.java
+++ b/core/java/android/credentials/ui/BaseDialogResult.java
@@ -56,14 +56,14 @@
* The intent extra key for the {@code BaseDialogResult} object when the credential
* selector activity finishes.
*/
- private static final String EXTRA_BASE_RESULT =
- "android.credentials.ui.extra.BASE_RESULT";
+ private static final String EXTRA_BASE_RESULT = "android.credentials.ui.extra.BASE_RESULT";
/** @hide **/
@IntDef(prefix = {"RESULT_CODE_"}, value = {
RESULT_CODE_DIALOG_USER_CANCELED,
RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS,
RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+ RESULT_CODE_DATA_PARSING_FAILURE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
@@ -80,6 +80,10 @@
* {@code resultData}.
*/
public static final int RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION = 2;
+ /**
+ * The UI was canceled because it failed to parse the incoming data.
+ */
+ public static final int RESULT_CODE_DATA_PARSING_FAILURE = 3;
@NonNull
private final IBinder mRequestToken;
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index ff6e897..c9fc722 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -1246,7 +1246,7 @@
* between frames.</p>
*
* <p>The timestamps match the timestamps of the output surfaces with readout timestamp
- * enabled (via {@link OutputConfiguration#useReadoutTimestamp}) if:</p>
+ * enabled (via {@link OutputConfiguration#setReadoutTimestampEnabled}) if:</p>
* <ul>
* <li> Timestamp base is {@link OutputConfiguration#TIMESTAMP_BASE_DEFAULT} and the
* output
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 11b80cc..f20b25f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -4600,7 +4600,7 @@
* {@link CameraCaptureSession.CaptureCallback#onCaptureStarted }.</p>
* <p>In addition, the application can switch an output surface's timestamp from start of
* exposure to start of readout by calling
- * {@link android.hardware.camera2.params.OutputConfiguration#useReadoutTimestamp }.</p>
+ * {@link android.hardware.camera2.params.OutputConfiguration#setReadoutTimestampEnabled }.</p>
* <p>The readout timestamp is beneficial for video recording, because the encoder favors
* uniform timestamps, and the readout timestamps better reflect the cadence camera sensors
* output data.</p>
@@ -5688,4 +5688,5 @@
+
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 577c8a3..42aa608 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -902,8 +902,8 @@
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td>Preview with video call</td> </tr>
* <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>MultI-purpose stream with JPEG or YUV still capture</td> </tr>
* <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>YUV and JPEG concurrent still image capture (for testing)</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG or YUV video snapshot</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG or YUV still image capture</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG video snapshot</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG still image capture</td> </tr>
* </table><br>
* </p>
*
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3d83009..381c87d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -4198,4 +4198,5 @@
+
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index dad7d3e..635e79c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -5699,4 +5699,5 @@
+
}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 3fc44f8..86c453b 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1225,14 +1225,6 @@
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
- STREAM_USE_CASE_RECORD),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
- STREAM_USE_CASE_STILL_CAPTURE)},
- "Preview, video record and YUV video snapshot"),
- new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
STREAM_USE_CASE_RECORD),
new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
@@ -1241,27 +1233,11 @@
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
- STREAM_USE_CASE_RECORD),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
- STREAM_USE_CASE_STILL_CAPTURE)},
- "Preview, in-application video processing and YUV video snapshot"),
- new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
STREAM_USE_CASE_PREVIEW),
new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
STREAM_USE_CASE_STILL_CAPTURE)},
"Preview, in-application image processing, and JPEG still image capture"),
- new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
- STREAM_USE_CASE_STILL_CAPTURE)},
- "Preview, in-application image processing, and YUV still image capture"),
};
private static StreamCombinationTemplate sCroppedRawStreamUseCaseCombinations[] = {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 8b7c5ec..857f62d 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -257,7 +257,7 @@
/**
* Timestamp is the start of readout in the same time domain as TIMESTAMP_BASE_SENSOR.
*
- * <p>NOTE: do not use! Use useReadoutTimestamp instead.</p>
+ * <p>NOTE: do not use! Use setReadoutTimestampEnabled instead.</p>
*
* @hide
*/
@@ -574,7 +574,7 @@
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mTimestampBase = TIMESTAMP_BASE_DEFAULT;
mMirrorMode = MIRROR_MODE_AUTO;
- mUseReadoutTimestamp = false;
+ mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
}
@@ -676,7 +676,7 @@
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
- mUseReadoutTimestamp = false;
+ mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
}
@@ -1050,7 +1050,7 @@
if (timestampBase == TIMESTAMP_BASE_READOUT_SENSOR) {
mTimestampBase = TIMESTAMP_BASE_SENSOR;
- mUseReadoutTimestamp = true;
+ mReadoutTimestampEnabled = true;
mIsReadoutSensorTimestampBase = true;
} else {
mTimestampBase = timestampBase;
@@ -1131,14 +1131,16 @@
* @param on The output image timestamp is the start of exposure time if false, and
* the start of readout time if true.
*/
- public void useReadoutTimestamp(boolean on) {
- mUseReadoutTimestamp = on;
+ public void setReadoutTimestampEnabled(boolean on) {
+ mReadoutTimestampEnabled = on;
}
/** Whether readout timestamp is used for this OutputConfiguration.
+ *
+ * @see #setReadoutTimestampEnabled
*/
- public boolean isReadoutTimestampUsed() {
- return mUseReadoutTimestamp;
+ public boolean isReadoutTimestampEnabled() {
+ return mReadoutTimestampEnabled;
}
/**
@@ -1172,7 +1174,7 @@
this.mStreamUseCase = other.mStreamUseCase;
this.mTimestampBase = other.mTimestampBase;
this.mMirrorMode = other.mMirrorMode;
- this.mUseReadoutTimestamp = other.mUseReadoutTimestamp;
+ this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
}
/**
@@ -1200,7 +1202,7 @@
int timestampBase = source.readInt();
int mirrorMode = source.readInt();
- boolean useReadoutTimestamp = source.readInt() == 1;
+ boolean readoutTimestampEnabled = source.readInt() == 1;
mSurfaceGroupId = surfaceSetId;
mRotation = rotation;
@@ -1229,7 +1231,7 @@
mStreamUseCase = streamUseCase;
mTimestampBase = timestampBase;
mMirrorMode = mirrorMode;
- mUseReadoutTimestamp = useReadoutTimestamp;
+ mReadoutTimestampEnabled = readoutTimestampEnabled;
}
/**
@@ -1350,7 +1352,7 @@
dest.writeLong(mStreamUseCase);
dest.writeInt(mTimestampBase);
dest.writeInt(mMirrorMode);
- dest.writeInt(mUseReadoutTimestamp ? 1 : 0);
+ dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
}
/**
@@ -1385,7 +1387,7 @@
mStreamUseCase != other.mStreamUseCase ||
mTimestampBase != other.mTimestampBase ||
mMirrorMode != other.mMirrorMode ||
- mUseReadoutTimestamp != other.mUseReadoutTimestamp)
+ mReadoutTimestampEnabled != other.mReadoutTimestampEnabled)
return false;
if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
return false;
@@ -1428,7 +1430,7 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase,
- mTimestampBase, mMirrorMode, mUseReadoutTimestamp ? 1 : 0);
+ mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
}
return HashCodeHelpers.hashCode(
@@ -1438,7 +1440,7 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
- mMirrorMode, mUseReadoutTimestamp ? 1 : 0);
+ mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
}
private static final String TAG = "OutputConfiguration";
@@ -1480,8 +1482,8 @@
private int mTimestampBase;
// Mirroring mode
private int mMirrorMode;
- // Use readout timestamp
- private boolean mUseReadoutTimestamp;
+ // readout timestamp
+ private boolean mReadoutTimestampEnabled;
// Whether the timestamp base is set to READOUT_SENSOR
private boolean mIsReadoutSensorTimestampBase;
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b333f5a..08238ca 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1301,6 +1301,54 @@
}
/**
+ * Sets the HDR conversion mode for the device.
+ *
+ * @param hdrConversionMode The {@link HdrConversionMode} to set.
+ * Note, {@code HdrConversionMode.preferredHdrOutputType} is only applicable when
+ * {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+ *
+ * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is not set
+ * when hdrConversionMode.conversionMode is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+ * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is set but
+ * hdrConversionMode.conversionMode is not {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+ *
+ * @see #getHdrConversionMode
+ * @see #getSupportedHdrOutputTypes
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_HDR_CONVERSION_MODE)
+ public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+ mGlobal.setHdrConversionMode(hdrConversionMode);
+ }
+
+ /**
+ * Returns the {@link HdrConversionMode} of the device, which is set by the user.
+ *
+ * @see #setHdrConversionMode
+ * @see #getSupportedHdrOutputTypes
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public HdrConversionMode getHdrConversionMode() {
+ return mGlobal.getHdrConversionMode();
+ }
+
+ /**
+ * Returns the HDR output types supported by the device.
+ *
+ * @see #getHdrConversionMode
+ * @see #setHdrConversionMode
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public @HdrType int[] getSupportedHdrOutputTypes() {
+ return mGlobal.getSupportedHdrOutputTypes();
+ }
+
+ /**
* When enabled the app requested mode is always selected regardless of user settings and
* policies for low brightness, low battery, etc.
*
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f038c66..d9db177 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -979,6 +979,39 @@
}
/**
+ * Sets the {@link HdrConversionMode} for the device.
+ */
+ public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+ try {
+ mDm.setHdrConversionMode(hdrConversionMode);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the {@link HdrConversionMode} of the device.
+ */
+ public HdrConversionMode getHdrConversionMode() {
+ try {
+ return mDm.getHdrConversionMode();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the HDR output types supported by the device.
+ */
+ public @HdrType int[] getSupportedHdrOutputTypes() {
+ try {
+ return mDm.getSupportedHdrOutputTypes();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* When enabled the app requested display resolution and refresh rate is always selected
* in DisplayModeDirector regardless of user settings and policies for low brightness, low
* battery etc.
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 7409187..9a092a5 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -21,6 +21,7 @@
import android.companion.virtual.IVirtualDevice;
import android.graphics.Point;
import android.hardware.SensorManager;
+import android.hardware.input.HostUsiVersion;
import android.os.Handler;
import android.os.PowerManager;
import android.util.IntArray;
@@ -403,6 +404,14 @@
public abstract SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(int displayId);
/**
+ * Get the version of the Universal Stylus Initiative (USI) Protocol supported by the display.
+ * @param displayId The id of the display.
+ * @return The USI version, or null if not supported
+ */
+ @Nullable
+ public abstract HostUsiVersion getHostUsiVersion(int displayId);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl b/core/java/android/hardware/display/HdrConversionMode.aidl
similarity index 65%
copy from core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
copy to core/java/android/hardware/display/HdrConversionMode.aidl
index 124a319..ac89dd6 100644
--- a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
+++ b/core/java/android/hardware/display/HdrConversionMode.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-package android.credentials;
+package android.hardware.display;
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IRegisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+parcelable HdrConversionMode;
\ No newline at end of file
diff --git a/core/java/android/hardware/display/HdrConversionMode.java b/core/java/android/hardware/display/HdrConversionMode.java
new file mode 100644
index 0000000..1accd17
--- /dev/null
+++ b/core/java/android/hardware/display/HdrConversionMode.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Display;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes the HDR conversion mode for a device.
+ *
+ * This class is used when user changes the HDR conversion mode of the device via
+ * {@link DisplayManager#setHdrConversionMode(HdrConversionMode)}.
+ * <p>
+ * HDR conversion mode has a conversionMode and preferredHdrOutputType. </p><p>
+ * The conversionMode can be one of:
+ * HDR_CONVERSION_PASSSTHROUGH : HDR conversion is disabled. The output HDR type will change
+ * dynamically to match the content. In this mode, preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_AUTO: The output HDR type is selected by the implementation. In this mode,
+ * preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_FORCE : The implementation converts all content to this HDR type, when possible.
+ * In this mode, preferredHdrOutputType should be set.
+ * </p>
+ * @hide
+ */
+@TestApi
+public final class HdrConversionMode implements Parcelable {
+ /** HDR output conversion is disabled */
+ public static final int HDR_CONVERSION_PASSTHROUGH = 1;
+ /** HDR output conversion is managed by the device manufacturer's implementation. */
+ public static final int HDR_CONVERSION_SYSTEM = 2;
+ /**
+ * HDR output conversion is set by the user. The preferred output type must be
+ * set in this case.
+ */
+ public static final int HDR_CONVERSION_FORCE = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"HDR_CONVERSION"}, value = {
+ HDR_CONVERSION_PASSTHROUGH,
+ HDR_CONVERSION_SYSTEM,
+ HDR_CONVERSION_FORCE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConversionMode {}
+
+ public static final @NonNull
+ Parcelable.Creator<HdrConversionMode> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public HdrConversionMode createFromParcel(Parcel source) {
+ return new HdrConversionMode(source);
+ }
+
+ @Override
+ public HdrConversionMode[] newArray(int size) {
+ return new HdrConversionMode[size];
+ }
+ };
+
+ private final @ConversionMode int mConversionMode;
+ private @Display.HdrCapabilities.HdrType int mPreferredHdrOutputType;
+
+ public HdrConversionMode(int conversionMode, int preferredHdrOutputType) {
+ if (conversionMode != HdrConversionMode.HDR_CONVERSION_FORCE
+ && preferredHdrOutputType != -1) {
+ throw new IllegalArgumentException("preferredHdrOutputType must not be set if"
+ + " the conversion mode is not HDR_CONVERSION_FORCE");
+ }
+
+ mConversionMode = conversionMode;
+ mPreferredHdrOutputType = preferredHdrOutputType;
+ }
+
+ public HdrConversionMode(int conversionMode) {
+ mConversionMode = conversionMode;
+ mPreferredHdrOutputType = Display.HdrCapabilities.HDR_TYPE_INVALID;
+ }
+
+ private HdrConversionMode(Parcel source) {
+ this(source.readInt(), source.readInt());
+ }
+
+ public int getConversionMode() {
+ return mConversionMode;
+ }
+
+ public int getPreferredHdrOutputType() {
+ return mPreferredHdrOutputType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mConversionMode);
+ dest.writeInt(mPreferredHdrOutputType);
+ }
+}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 28bb35f..0a44f85 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -23,6 +23,7 @@
import android.hardware.display.BrightnessInfo;
import android.hardware.display.Curve;
import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.hardware.display.HdrConversionMode;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
@@ -174,6 +175,14 @@
Mode getUserPreferredDisplayMode(int displayId);
Mode getSystemPreferredDisplayMode(int displayId);
+ // Sets the HDR conversion mode for a device.
+ // Requires MODIFY_HDR_CONVERSION_MODE permission.
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MODIFY_HDR_CONVERSION_MODE)")
+ void setHdrConversionMode(in HdrConversionMode hdrConversionMode);
+ HdrConversionMode getHdrConversionMode();
+ int[] getSupportedHdrOutputTypes();
+
// When enabled the app requested display resolution and refresh rate is always selected
// in DisplayModeDirector regardless of user settings and policies for low brightness, low
// battery etc.
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 197739b..96098f8 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -26,6 +26,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
@@ -87,6 +88,7 @@
private CryptoObject mCryptoObject;
private Face mRemovalFace;
private Handler mHandler;
+ private List<FaceSensorPropertiesInternal> mProps = new ArrayList<>();
private final IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
@@ -168,6 +170,16 @@
Slog.v(TAG, "FaceAuthenticationManagerService was null");
}
mHandler = new MyHandler(context);
+ if (context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED) {
+ addAuthenticatorsRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ @NonNull List<FaceSensorPropertiesInternal> sensors) {
+ mProps = sensors;
+ }
+ });
+ }
}
/**
@@ -664,14 +676,14 @@
@NonNull
public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal() {
try {
- if (mService == null) {
- return new ArrayList<>();
+ if (!mProps.isEmpty() || mService == null) {
+ return mProps;
}
return mService.getSensorPropertiesInternal(mContext.getOpPackageName());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
- return new ArrayList<>();
+ return mProps;
}
/**
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b24b30e..d9f8f8e 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -153,6 +153,7 @@
@Nullable private RemoveTracker mRemoveTracker;
private Handler mHandler;
@Nullable private float[] mEnrollStageThresholds;
+ private List<FingerprintSensorPropertiesInternal> mProps = new ArrayList<>();
/**
* Retrieves a list of properties for all fingerprint sensors on the device.
@@ -1185,8 +1186,8 @@
@NonNull
public List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal() {
try {
- if (mService == null) {
- return new ArrayList<>();
+ if (!mProps.isEmpty() || mService == null) {
+ return mProps;
}
return mService.getSensorPropertiesInternal(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1502,6 +1503,17 @@
Slog.v(TAG, "FingerprintService was null");
}
mHandler = new MyHandler(context);
+ if (context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED) {
+ addAuthenticatorsRegisteredCallback(
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ @NonNull List<FingerprintSensorPropertiesInternal> sensors) {
+ mProps = sensors;
+ }
+ });
+ }
}
private int getCurrentUserId() {
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index 5bb1f03..5615418 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -15,14 +15,20 @@
*/
package android.hardware.hdmi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import androidx.annotation.IntRange;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
- * A class to encapsulate HDMI port information. Contains the capability of the ports such as
+ * Encapsulates HDMI port information. Contains the capability of the ports such as
* HDMI-CEC, MHL, ARC(Audio Return Channel), eARC and physical address assigned to each port.
*
* @hide
@@ -35,6 +41,18 @@
/** HDMI port type: Output */
public static final int PORT_OUTPUT = 1;
+ /**
+ * @hide
+ *
+ * @see HdmiPortInfo#getType()
+ */
+ @IntDef(prefix = { "PORT_" }, value = {
+ PORT_INPUT,
+ PORT_OUTPUT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PortType {}
+
private final int mId;
private final int mType;
private final int mAddress;
@@ -46,43 +64,48 @@
/**
* Constructor.
*
- * @param id identifier assigned to each port. 1 for HDMI port 1
+ * @param id identifier assigned to each port. 1 for HDMI OUT port 1
* @param type HDMI port input/output type
* @param address physical address of the port
* @param cec {@code true} if HDMI-CEC is supported on the port
* @param mhl {@code true} if MHL is supported on the port
* @param arc {@code true} if audio return channel is supported on the port
- */
- public HdmiPortInfo(int id, int type, int address, boolean cec, boolean mhl, boolean arc) {
- this(id, type, address, cec, mhl, arc, false);
- }
-
- /**
- * Constructor.
*
- * @param id identifier assigned to each port. 1 for HDMI port 1
- * @param type HDMI port input/output type
- * @param address physical address of the port
- * @param cec {@code true} if HDMI-CEC is supported on the port
- * @param mhl {@code true} if MHL is supported on the port
- * @param arc {@code true} if audio return channel is supported on the port
- * @param earc {@code true} if eARC is supported on the port
+ * @deprecated use {@link Builder()} instead
*/
- public HdmiPortInfo(int id, int type, int address,
- boolean cec, boolean mhl, boolean arc, boolean earc) {
+ @Deprecated
+ public HdmiPortInfo(int id, @PortType int type,
+ @IntRange(from = 0) int address, boolean cec, boolean mhl, boolean arc) {
mId = id;
mType = type;
mAddress = address;
mCecSupported = cec;
mArcSupported = arc;
- mEarcSupported = earc;
+ mEarcSupported = false;
mMhlSupported = mhl;
}
/**
- * Returns the port id.
+ * Converts an instance to a builder
*
- * @return port id
+ * @hide
+ */
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ private HdmiPortInfo(Builder builder) {
+ this.mId = builder.mId;
+ this.mType = builder.mType;
+ this.mAddress = builder.mAddress;
+ this.mCecSupported = builder.mCecSupported;
+ this.mArcSupported = builder.mArcSupported;
+ this.mEarcSupported = builder.mEarcSupported;
+ this.mMhlSupported = builder.mMhlSupported;
+ }
+
+ /**
+ * Returns the port id.
*/
public int getId() {
return mId;
@@ -90,26 +113,22 @@
/**
* Returns the port type.
- *
- * @return port type
*/
+ @PortType
public int getType() {
return mType;
}
/**
* Returns the port address.
- *
- * @return port address
*/
+ @IntRange(from = 0)
public int getAddress() {
return mAddress;
}
/**
* Returns {@code true} if the port supports HDMI-CEC signaling.
- *
- * @return {@code true} if the port supports HDMI-CEC signaling.
*/
public boolean isCecSupported() {
return mCecSupported;
@@ -117,8 +136,6 @@
/**
* Returns {@code true} if the port supports MHL signaling.
- *
- * @return {@code true} if the port supports MHL signaling.
*/
public boolean isMhlSupported() {
return mMhlSupported;
@@ -126,8 +143,6 @@
/**
* Returns {@code true} if the port supports audio return channel.
- *
- * @return {@code true} if the port supports audio return channel
*/
public boolean isArcSupported() {
return mArcSupported;
@@ -135,8 +150,6 @@
/**
* Returns {@code true} if the port supports eARC.
- *
- * @return {@code true} if the port supports eARC.
*/
public boolean isEarcSupported() {
return mEarcSupported;
@@ -166,7 +179,12 @@
boolean arc = (source.readInt() == 1);
boolean mhl = (source.readInt() == 1);
boolean earc = (source.readInt() == 1);
- return new HdmiPortInfo(id, type, address, cec, mhl, arc, earc);
+ return new Builder(id, type, address)
+ .setCecSupported(cec)
+ .setArcSupported(arc)
+ .setEarcSupported(earc)
+ .setMhlSupported(mhl)
+ .build();
}
@Override
@@ -225,4 +243,95 @@
return java.util.Objects.hash(
mId, mType, mAddress, mCecSupported, mArcSupported, mMhlSupported, mEarcSupported);
}
+
+ /**
+ * Builder for {@link HdmiPortInfo} instances.
+ */
+ public static final class Builder {
+ // Required parameters
+ private int mId;
+ private int mType;
+ private int mAddress;
+
+ // Optional parameters that are set to false by default.
+ private boolean mCecSupported;
+ private boolean mArcSupported;
+ private boolean mEarcSupported;
+ private boolean mMhlSupported;
+
+ /**
+ * Constructor
+ *
+ * @param id identifier assigned to each port. 1 for HDMI OUT port 1
+ * @param type HDMI port input/output type
+ * @param address physical address of the port
+ * @throws IllegalArgumentException if the parameters are invalid
+ */
+ public Builder(int id, @PortType int type, @IntRange(from = 0) int address) {
+ if (type != PORT_INPUT && type != PORT_OUTPUT) {
+ throw new IllegalArgumentException(
+ "type should be " + PORT_INPUT + " or " + PORT_OUTPUT + ".");
+ }
+ if (address < 0) {
+ throw new IllegalArgumentException("address should be positive.");
+ }
+ mId = id;
+ mType = type;
+ mAddress = address;
+ }
+
+ private Builder(@NonNull HdmiPortInfo hdmiPortInfo) {
+ mId = hdmiPortInfo.mId;
+ mType = hdmiPortInfo.mType;
+ mAddress = hdmiPortInfo.mAddress;
+ mCecSupported = hdmiPortInfo.mCecSupported;
+ mArcSupported = hdmiPortInfo.mArcSupported;
+ mEarcSupported = hdmiPortInfo.mEarcSupported;
+ mMhlSupported = hdmiPortInfo.mMhlSupported;
+ }
+
+ /**
+ * Create a new {@link HdmiPortInfo} object.
+ */
+ @NonNull
+ public HdmiPortInfo build() {
+ return new HdmiPortInfo(this);
+ }
+
+ /**
+ * Sets the value for whether the port supports HDMI-CEC signaling.
+ */
+ @NonNull
+ public Builder setCecSupported(boolean supported) {
+ mCecSupported = supported;
+ return this;
+ }
+
+ /**
+ * Sets the value for whether the port supports audio return channel.
+ */
+ @NonNull
+ public Builder setArcSupported(boolean supported) {
+ mArcSupported = supported;
+ return this;
+ }
+
+ /**
+ * Sets the value for whether the port supports eARC.
+ */
+ @NonNull
+ public Builder setEarcSupported(boolean supported) {
+ mEarcSupported = supported;
+ return this;
+ }
+
+ /**
+ * Sets the value for whether the port supports MHL signaling.
+ */
+ @NonNull
+ public Builder setMhlSupported(boolean supported) {
+ mMhlSupported = supported;
+ return this;
+ }
+ }
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 222cf08..c3fae55 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -17,6 +17,7 @@
package android.hardware.input;
import android.graphics.Rect;
+import android.hardware.input.HostUsiVersion;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.IInputDevicesChangedListener;
@@ -233,4 +234,6 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT)")
void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener);
+
+ HostUsiVersion getHostUsiVersionFromDisplayConfig(int displayId);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index f27c34c..3ccc940 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -55,8 +55,6 @@
import android.os.Vibrator;
import android.os.VibratorManager;
import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.util.DisplayUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -71,7 +69,6 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
@@ -102,19 +99,33 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final IInputManager mIm;
- private final boolean mIsStylusPointerIconEnabled;
+ /**
+ * InputManager has historically used its own static getter {@link #getInstance()} that doesn't
+ * provide a context. We provide a Context to the InputManager instance through the
+ * {@link android.app.SystemServiceRegistry}. Methods that need a Context must use
+ * {@link #getContext()} to obtain it.
+ */
+ @Nullable
+ private Context mLateInitContext;
+
+ /**
+ * Whether a PointerIcon is shown for stylus pointers.
+ * Obtain using {@link #isStylusPointerIconEnabled()}.
+ */
+ @Nullable
+ private Boolean mIsStylusPointerIconEnabled = null;
// Guarded by mInputDevicesLock
private final Object mInputDevicesLock = new Object();
private SparseArray<InputDevice> mInputDevices;
private InputDevicesChangedListener mInputDevicesChangedListener;
- private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
- new ArrayList<InputDeviceListenerDelegate>();
+ private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = new ArrayList<>();
// Guarded by mTabletModeLock
private final Object mTabletModeLock = new Object();
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private TabletModeChangedListener mTabletModeChangedListener;
- private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
+ private ArrayList<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
private final Object mBatteryListenersLock = new Object();
// Maps a deviceId whose battery is currently being monitored to an entry containing the
@@ -126,7 +137,7 @@
private final Object mKeyboardBacklightListenerLock = new Object();
@GuardedBy("mKeyboardBacklightListenerLock")
- private List<KeyboardBacklightListenerDelegate> mKeyboardBacklightListeners;
+ private ArrayList<KeyboardBacklightListenerDelegate> mKeyboardBacklightListeners;
@GuardedBy("mKeyboardBacklightListenerLock")
private IKeyboardBacklightListener mKeyboardBacklightListener;
@@ -326,15 +337,6 @@
} catch (RemoteException ex) {
Log.w(TAG, "Could not get VelocityTracker strategy: " + ex);
}
-
- // TODO(b/266013036): Pass a Context into InputManager constructor.
- final Context context = ActivityThread.currentApplication();
- if (context != null) {
- mIsStylusPointerIconEnabled = context.getResources()
- .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon);
- } else {
- mIsStylusPointerIconEnabled = false;
- }
}
/**
@@ -368,24 +370,47 @@
* Gets an instance of the input manager.
*
* @return The input manager instance.
- *
+ * @deprecated Use {@link Context#getSystemService(Class)} or {@link #getInstance(Context)}
+ * to obtain the InputManager instance.
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static InputManager getInstance() {
+ return getInstance(ActivityThread.currentApplication());
+ }
+
+ /**
+ * Gets an instance of the input manager.
+ *
+ * @return The input manager instance.
+ * @hide
+ */
+ public static InputManager getInstance(Context context) {
synchronized (InputManager.class) {
if (sInstance == null) {
try {
sInstance = new InputManager(IInputManager.Stub
.asInterface(ServiceManager.getServiceOrThrow(Context.INPUT_SERVICE)));
+
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
+ if (sInstance.mLateInitContext == null) {
+ sInstance.mLateInitContext = context;
+ }
return sInstance;
}
}
+ @NonNull
+ private Context getContext() {
+ return Objects.requireNonNull(mLateInitContext,
+ "A context is required for InputManager. Get the InputManager instance using "
+ + "Context#getSystemService before calling this method.");
+ }
+
/**
* Get the current VelocityTracker strategy. Only works when the system has fully booted up.
* @hide
@@ -499,7 +524,7 @@
/**
* Enables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission#DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -518,7 +543,7 @@
/**
* Disables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission#DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -975,6 +1000,7 @@
*/
@TestApi
@NonNull
+ @SuppressWarnings("unchecked")
@RequiresPermission(Manifest.permission.REMAP_MODIFIER_KEYS)
public Map<Integer, Integer> getModifierKeyRemapping() {
try {
@@ -1005,7 +1031,7 @@
* Sets the TouchCalibration to apply to the specified input device's coordinates.
* <p>
* This method may have the side-effect of causing the input device in question
- * to be reconfigured. Requires {@link android.Manifest.permission.SET_INPUT_CALIBRATION}.
+ * to be reconfigured. Requires {@link android.Manifest.permission#SET_INPUT_CALIBRATION}.
* </p>
*
* @param inputDeviceDescriptor The input device descriptor.
@@ -1121,19 +1147,14 @@
* @hide
*/
public int getPointerSpeed(Context context) {
- int speed = DEFAULT_POINTER_SPEED;
- try {
- speed = Settings.System.getInt(context.getContentResolver(),
- Settings.System.POINTER_SPEED);
- } catch (SettingNotFoundException snfe) {
- }
- return speed;
+ return Settings.System.getInt(context.getContentResolver(),
+ Settings.System.POINTER_SPEED, DEFAULT_POINTER_SPEED);
}
/**
* Sets the mouse pointer speed.
* <p>
- * Requires {@link android.Manifest.permission.WRITE_SETTINGS}.
+ * Requires {@link android.Manifest.permission#WRITE_SETTINGS}.
* </p>
*
* @param context The application context.
@@ -1154,7 +1175,7 @@
/**
* Changes the mouse pointer speed temporarily, but does not save the setting.
* <p>
- * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
+ * Requires {@link android.Manifest.permission#SET_POINTER_SPEED}.
* </p>
*
* @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
@@ -1188,8 +1209,7 @@
*/
@FloatRange(from = 0, to = 1)
public float getMaximumObscuringOpacityForTouch() {
- Context context = ActivityThread.currentApplication();
- return Settings.Global.getFloat(context.getContentResolver(),
+ return Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
}
@@ -1225,8 +1245,7 @@
throw new IllegalArgumentException(
"Maximum obscuring opacity for touch should be >= 0 and <= 1");
}
- Context context = ActivityThread.currentApplication();
- Settings.Global.putFloat(context.getContentResolver(),
+ Settings.Global.putFloat(getContext().getContentResolver(),
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
}
@@ -1307,7 +1326,7 @@
* The synchronization mode determines whether the method blocks while waiting for
* input injection to proceed.
* <p>
- * Requires the {@link android.Manifest.permission.INJECT_EVENTS} permission.
+ * Requires the {@link android.Manifest.permission#INJECT_EVENTS} permission.
* </p><p>
* Make sure you correctly set the event time and input source of the event
* before calling this method.
@@ -1315,9 +1334,9 @@
*
* @param event The event to inject.
* @param mode The synchronization mode. One of:
- * {@link android.os.InputEventInjectionSync.NONE},
- * {@link android.os.InputEventInjectionSync.WAIT_FOR_RESULT}, or
- * {@link android.os.InputEventInjectionSync.WAIT_FOR_FINISHED}.
+ * {@link android.os.InputEventInjectionSync#NONE},
+ * {@link android.os.InputEventInjectionSync#WAIT_FOR_RESULT}, or
+ * {@link android.os.InputEventInjectionSync#WAIT_FOR_FINISHED}.
* @param targetUid The uid to target, or {@link android.os.Process#INVALID_UID} to target all
* windows.
* @return True if input event injection succeeded.
@@ -1347,7 +1366,7 @@
* The synchronization mode determines whether the method blocks while waiting for
* input injection to proceed.
* <p>
- * Requires the {@link android.Manifest.permission.INJECT_EVENTS} permission.
+ * Requires the {@link android.Manifest.permission#INJECT_EVENTS} permission.
* </p><p>
* Make sure you correctly set the event time and input source of the event
* before calling this method.
@@ -1355,9 +1374,9 @@
*
* @param event The event to inject.
* @param mode The synchronization mode. One of:
- * {@link android.os.InputEventInjectionSync.NONE},
- * {@link android.os.InputEventInjectionSync.WAIT_FOR_RESULT}, or
- * {@link android.os.InputEventInjectionSync.WAIT_FOR_FINISHED}.
+ * {@link android.os.InputEventInjectionSync#NONE},
+ * {@link android.os.InputEventInjectionSync#WAIT_FOR_RESULT}, or
+ * {@link android.os.InputEventInjectionSync#WAIT_FOR_FINISHED}.
* @return True if input event injection succeeded.
*
* @hide
@@ -1381,7 +1400,8 @@
* {@link android.view.InputEvent}
* {@code null} if the event could not be verified.
*/
- public @Nullable VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) {
+ @Nullable
+ public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) {
try {
return mIm.verifyInputEvent(event);
} catch (RemoteException ex) {
@@ -1393,7 +1413,7 @@
* Changes the mouse pointer's icon shape into the specified id.
*
* @param iconId The id of the pointer graphic, as a value between
- * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_HANDWRITING}.
+ * {@link PointerIcon#TYPE_ARROW} and {@link PointerIcon#TYPE_HANDWRITING}.
*
* @hide
*/
@@ -1422,6 +1442,10 @@
* stylus pointer, false if there is no pointer icon shown for styluses.
*/
public boolean isStylusPointerIconEnabled() {
+ if (mIsStylusPointerIconEnabled == null) {
+ mIsStylusPointerIconEnabled = getContext().getResources()
+ .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon);
+ }
return mIsStylusPointerIconEnabled;
}
@@ -1543,7 +1567,7 @@
* @param inputPort The port of the input device.
* @param displayPort The physical port of the associated display.
* <p>
- * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1560,7 +1584,7 @@
* static association for the cleared input port will be restored.
* @param inputPort The port of the input device to be cleared.
* <p>
- * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1578,7 +1602,7 @@
* @param inputPort The port of the input device.
* @param displayUniqueId The unique id of the associated display.
* <p>
- * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1595,7 +1619,7 @@
* Removes a runtime association between the input device and display.
* @param inputPort The port of the input device.
* <p>
- * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission#ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1637,35 +1661,11 @@
// If there are no input devices that report a valid USI version, see if there is a config
// that specifies the USI version for the display. This is to handle cases where the USI
// input device is not registered by the kernel/driver all the time.
- return findConfigUsiVersionForDisplay(display);
- }
-
- private HostUsiVersion findConfigUsiVersionForDisplay(@NonNull Display display) {
- final Context context = Objects.requireNonNull(ActivityThread.currentApplication());
- final String[] displayUniqueIds = context.getResources().getStringArray(
- R.array.config_displayUniqueIdArray);
- final int index;
- if (displayUniqueIds.length == 0 && display.getDisplayId() == context.getDisplayId()) {
- index = 0;
- } else {
- index = DisplayUtils.getDisplayUniqueIdConfigIndex(context.getResources(),
- display.getUniqueId());
+ try {
+ return mIm.getHostUsiVersionFromDisplayConfig(display.getDisplayId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
-
- final String[] versions = context.getResources().getStringArray(
- R.array.config_displayUsiVersionArray);
- if (index < 0 || index >= versions.length) {
- return null;
- }
- final String version = versions[index];
- if (version == null || version.isEmpty()) {
- return null;
- }
- final String[] majorMinor = version.split("\\.");
- if (majorMinor.length != 2) {
- throw new IllegalStateException("Failed to parse USI version: " + version);
- }
- return new HostUsiVersion(Integer.parseInt(majorMinor[0]), Integer.parseInt(majorMinor[1]));
}
private void populateInputDevicesLocked() {
@@ -1687,9 +1687,9 @@
throw ex.rethrowFromSystemServer();
}
- mInputDevices = new SparseArray<InputDevice>();
- for (int i = 0; i < ids.length; i++) {
- mInputDevices.put(ids[i], null);
+ mInputDevices = new SparseArray<>();
+ for (int id : ids) {
+ mInputDevices.put(id, null);
}
}
}
@@ -1761,8 +1761,8 @@
+ "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode);
}
synchronized (mTabletModeLock) {
- final int N = mOnTabletModeChangedListeners.size();
- for (int i = 0; i < N; i++) {
+ final int numListeners = mOnTabletModeChangedListeners.size();
+ for (int i = 0; i < numListeners; i++) {
OnTabletModeChangedListenerDelegate listener =
mOnTabletModeChangedListeners.get(i);
listener.sendTabletModeChanged(whenNanos, inTabletMode);
@@ -1965,7 +1965,7 @@
}
List<LightState> lightStateList = request.getLightStates();
mIm.setLightStates(deviceId, lightIds,
- lightStateList.toArray(new LightState[lightStateList.size()]),
+ lightStateList.toArray(new LightState[0]),
token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2072,8 +2072,11 @@
} else {
// The deviceId is already being monitored for battery changes.
// Ensure that the listener is not already registered.
- for (InputDeviceBatteryListenerDelegate delegate : listenersForDevice.mDelegates) {
- if (Objects.equals(listener, delegate.mListener)) {
+ final int numDelegates = listenersForDevice.mDelegates.size();
+ for (int i = 0; i < numDelegates; i++) {
+ InputDeviceBatteryListener registeredListener =
+ listenersForDevice.mDelegates.get(i).mListener;
+ if (Objects.equals(listener, registeredListener)) {
throw new IllegalArgumentException(
"Attempting to register an InputDeviceBatteryListener that has "
+ "already been registered for deviceId: "
@@ -2208,7 +2211,7 @@
* Changes the touchpad pointer speed temporarily, but does not save the setting.
*
* The new speed will only apply to gesture-compatible touchpads.
- * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
+ * Requires {@link android.Manifest.permission#SET_POINTER_SPEED}.
*
* @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
* {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
@@ -2398,8 +2401,9 @@
throw e.rethrowFromSystemServer();
}
}
- for (KeyboardBacklightListenerDelegate delegate : mKeyboardBacklightListeners) {
- if (delegate.mListener == listener) {
+ final int numListeners = mKeyboardBacklightListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (mKeyboardBacklightListeners.get(i).mListener == listener) {
throw new IllegalArgumentException("Listener has already been registered!");
}
}
@@ -2572,21 +2576,19 @@
public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) {
SomeArgs args = SomeArgs.obtain();
- args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
+ args.argi1 = (int) whenNanos;
args.argi2 = (int) (whenNanos >> 32);
- args.arg1 = (Boolean) inTabletMode;
+ args.arg1 = inTabletMode;
obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget();
}
@Override
public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_TABLET_MODE_CHANGED:
- SomeArgs args = (SomeArgs) msg.obj;
- long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
- boolean inTabletMode = (boolean) args.arg1;
- mListener.onTabletModeChanged(whenNanos, inTabletMode);
- break;
+ if (msg.what == MSG_TABLET_MODE_CHANGED) {
+ SomeArgs args = (SomeArgs) msg.obj;
+ long whenNanos = (args.argi1 & 0xFFFFFFFFL) | ((long) args.argi2 << 32);
+ boolean inTabletMode = (boolean) args.arg1;
+ mListener.onTabletModeChanged(whenNanos, inTabletMode);
}
}
}
@@ -2654,8 +2656,10 @@
if (entry == null) return;
entry.mInputDeviceBatteryState = state;
- for (InputDeviceBatteryListenerDelegate delegate : entry.mDelegates) {
- delegate.notifyBatteryStateChanged(entry.mInputDeviceBatteryState);
+ final int numDelegates = entry.mDelegates.size();
+ for (int i = 0; i < numDelegates; i++) {
+ entry.mDelegates.get(i)
+ .notifyBatteryStateChanged(entry.mInputDeviceBatteryState);
}
}
}
@@ -2668,8 +2672,8 @@
private final int mBrightnessLevel;
private final int mMaxBrightnessLevel;
- LocalKeyboardBacklightState(int brightnesslevel, int maxBrightnessLevel) {
- mBrightnessLevel = brightnesslevel;
+ LocalKeyboardBacklightState(int brightnessLevel, int maxBrightnessLevel) {
+ mBrightnessLevel = brightnessLevel;
mMaxBrightnessLevel = maxBrightnessLevel;
}
@@ -2709,8 +2713,10 @@
boolean isTriggeredByKeyPress) {
synchronized (mKeyboardBacklightListenerLock) {
if (mKeyboardBacklightListeners == null) return;
- for (KeyboardBacklightListenerDelegate delegate : mKeyboardBacklightListeners) {
- delegate.notifyKeyboardBacklightChange(deviceId, state, isTriggeredByKeyPress);
+ final int numListeners = mKeyboardBacklightListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mKeyboardBacklightListeners.get(i)
+ .notifyKeyboardBacklightChange(deviceId, state, isTriggeredByKeyPress);
}
}
}
diff --git a/core/java/android/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS
index e5d0370..01b2cb9 100644
--- a/core/java/android/hardware/soundtrigger/OWNERS
+++ b/core/java/android/hardware/soundtrigger/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
elaurent@google.com
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 621eab5..b7a694c 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -2017,26 +2017,14 @@
* - {@link #STATUS_BAD_VALUE} if modules is null
* - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
*
- * @deprecated Please use {@link #listModulesAsOriginator(ArrayList, Identity)} or
+ * @removed Please use {@link #listModulesAsOriginator(ArrayList, Identity)} or
* {@link #listModulesAsMiddleman(ArrayList, Identity, Identity)}, based on whether the
* client is acting on behalf of its own identity or a separate identity.
* @hide
*/
@UnsupportedAppUsage
public static int listModules(@NonNull ArrayList<ModuleProperties> modules) {
- // TODO(ytai): This is a temporary hack to retain prior behavior, which makes
- // assumptions about process affinity and Binder context, namely that the binder calling ID
- // reliably reflects the originator identity.
- Identity middlemanIdentity = new Identity();
- middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
-
- Identity originatorIdentity = new Identity();
- originatorIdentity.pid = Binder.getCallingPid();
- originatorIdentity.uid = Binder.getCallingUid();
-
- try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- return listModulesAsMiddleman(modules, middlemanIdentity, originatorIdentity);
- }
+ return STATUS_OK;
}
/**
@@ -2051,10 +2039,11 @@
* - {@link #STATUS_NO_INIT} if the native service cannot be reached
* - {@link #STATUS_BAD_VALUE} if modules is null
* - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
- *
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
* @hide
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
+ @Deprecated
public static int listModulesAsOriginator(@NonNull ArrayList<ModuleProperties> modules,
@NonNull Identity originatorIdentity) {
try {
@@ -2082,9 +2071,11 @@
* - {@link #STATUS_BAD_VALUE} if modules is null
* - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
*
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
* @hide
*/
@RequiresPermission(SOUNDTRIGGER_DELEGATE_IDENTITY)
+ @Deprecated
public static int listModulesAsMiddleman(@NonNull ArrayList<ModuleProperties> modules,
@NonNull Identity middlemanIdentity,
@NonNull Identity originatorIdentity) {
@@ -2123,30 +2114,14 @@
* is OK.
* @return a valid sound module in case of success or null in case of error.
*
- * @deprecated Please use
- * {@link #attachModuleAsOriginator(int, StatusListener, Handler, Identity)} or
- * {@link #attachModuleAsMiddleman(int, StatusListener, Handler, Identity, Identity)}, based
- * on whether the client is acting on behalf of its own identity or a separate identity.
+ * @removed Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
* @hide
*/
@UnsupportedAppUsage
private static SoundTriggerModule attachModule(int moduleId,
@NonNull StatusListener listener,
@Nullable Handler handler) {
- // TODO(ytai): This is a temporary hack to retain prior behavior, which makes
- // assumptions about process affinity and Binder context, namely that the binder calling ID
- // reliably reflects the originator identity.
- Identity middlemanIdentity = new Identity();
- middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
-
- Identity originatorIdentity = new Identity();
- originatorIdentity.pid = Binder.getCallingPid();
- originatorIdentity.uid = Binder.getCallingUid();
-
- try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- return attachModuleAsMiddleman(moduleId, listener, handler, middlemanIdentity,
- originatorIdentity);
- }
+ return null;
}
/**
@@ -2162,10 +2137,11 @@
* @param originatorIdentity The identity of the originator, which will be used for permission
* purposes.
* @return a valid sound module in case of success or null in case of error.
- *
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
* @hide
*/
@RequiresPermission(SOUNDTRIGGER_DELEGATE_IDENTITY)
+ @Deprecated
public static SoundTriggerModule attachModuleAsMiddleman(int moduleId,
@NonNull SoundTrigger.StatusListener listener,
@Nullable Handler handler, Identity middlemanIdentity,
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index eed92c1..a1d74df 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -102,7 +102,9 @@
* Detach from this module. The {@link SoundTrigger.StatusListener} callback will not be called
* anymore and associated resources will be released.
* All models must have been unloaded prior to detaching.
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public synchronized void detach() {
try {
@@ -131,7 +133,9 @@
* - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
* service fails
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public synchronized int loadSoundModel(@NonNull SoundTrigger.SoundModel model,
@NonNull int[] soundModelHandle) {
@@ -191,8 +195,10 @@
* - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
* - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
* service fails
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
*/
@UnsupportedAppUsage
+ @Deprecated
public synchronized int unloadSoundModel(int soundModelHandle) {
try {
mService.unloadModel(soundModelHandle);
@@ -219,8 +225,10 @@
* - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
* service fails
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
*/
@UnsupportedAppUsage
+ @Deprecated
public synchronized int startRecognition(int soundModelHandle,
SoundTrigger.RecognitionConfig config) {
try {
@@ -244,8 +252,10 @@
* - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
* service fails
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
+ * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
*/
@UnsupportedAppUsage
+ @Deprecated
public synchronized int stopRecognition(int soundModelHandle) {
try {
mService.stopRecognition(soundModelHandle);
diff --git a/core/java/android/hardware/usb/DisplayPortAltModeInfo.java b/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
index febc643..7c62373 100644
--- a/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
+++ b/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
@@ -39,6 +39,8 @@
private final @DisplayPortAltModeStatus int mPartnerSinkStatus;
private final @DisplayPortAltModeStatus int mCableStatus;
private final int mNumLanes;
+ private final boolean mHotPlugDetect;
+ private final @LinkTrainingStatus int mLinkTrainingStatus;
/**
* Port Partners:
@@ -88,6 +90,25 @@
*/
public static final int DISPLAYPORT_ALT_MODE_STATUS_ENABLED = 3;
+ /*
+ * Indicates that DisplayPort Alt Mode link training has not initiated or completed.
+ */
+ public static final int LINK_TRAINING_STATUS_UNKNOWN = 0;
+
+ /*
+ * Indicates that DisplayPort Alt Mode link training has completed and the optimal data
+ * transmission settings between the device and the connected port partner have successfully
+ * been negotiated.
+ */
+ public static final int LINK_TRAINING_STATUS_SUCCESS = 1;
+
+ /*
+ * Indicates that DisplayPort Alt Mode link training has completed but no data transmission
+ * settings between the device and the connected port partner could be established, and that the
+ * link initialization has terminated.
+ */
+ public static final int LINK_TRAINING_STATUS_FAILURE = 2;
+
/** @hide */
@IntDef(prefix = { "DISPLAYPORT_ALT_MODE_STATUS_" }, value = {
DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
@@ -99,18 +120,31 @@
public @interface DisplayPortAltModeStatus {}
/** @hide */
+ @IntDef(prefix = { "LINK_TRAINING_STATUS_" }, value = {
+ LINK_TRAINING_STATUS_UNKNOWN,
+ LINK_TRAINING_STATUS_SUCCESS,
+ LINK_TRAINING_STATUS_FAILURE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LinkTrainingStatus {}
+
+ /** @hide */
public DisplayPortAltModeInfo() {
mPartnerSinkStatus = DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
mCableStatus = DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
mNumLanes = 0;
+ mHotPlugDetect = false;
+ mLinkTrainingStatus = LINK_TRAINING_STATUS_UNKNOWN;
}
/** @hide */
public DisplayPortAltModeInfo(int partnerSinkStatus, int cableStatus,
- int numLanes) {
+ int numLanes, boolean hotPlugDetect, int linkTrainingStatus) {
mPartnerSinkStatus = partnerSinkStatus;
mCableStatus = cableStatus;
mNumLanes = numLanes;
+ mHotPlugDetect = hotPlugDetect;
+ mLinkTrainingStatus = linkTrainingStatus;
}
/**
@@ -145,6 +179,21 @@
return mNumLanes;
}
+ /**
+ * Returns whether or not the Hot Plug Detect (HPD) value of the connected DisplayPort Alt Mode
+ * partner sink is active.
+ */
+ public boolean isHotPlugDetectActive() {
+ return mHotPlugDetect;
+ }
+
+ /**
+ * Returns the DisplayPort Alt Mode link training status.
+ */
+ public @LinkTrainingStatus int getLinkTrainingStatus() {
+ return mLinkTrainingStatus;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -155,6 +204,8 @@
dest.writeInt(mPartnerSinkStatus);
dest.writeInt(mCableStatus);
dest.writeInt(mNumLanes);
+ dest.writeBoolean(mHotPlugDetect);
+ dest.writeInt(mLinkTrainingStatus);
}
@NonNull
@@ -166,6 +217,10 @@
+ mCableStatus
+ " numLanes="
+ mNumLanes
+ + " hotPlugDetect="
+ + mHotPlugDetect
+ + " linkTrainingStatus="
+ + mLinkTrainingStatus
+ "}";
}
@@ -180,12 +235,15 @@
DisplayPortAltModeInfo other = (DisplayPortAltModeInfo) o;
return this.mPartnerSinkStatus == other.mPartnerSinkStatus
&& this.mCableStatus == other.mCableStatus
- && this.mNumLanes == other.mNumLanes;
+ && this.mNumLanes == other.mNumLanes
+ && this.mHotPlugDetect == other.mHotPlugDetect
+ && this.mLinkTrainingStatus == other.mLinkTrainingStatus;
}
@Override
public int hashCode() {
- return Objects.hash(mPartnerSinkStatus, mCableStatus, mNumLanes);
+ return Objects.hash(mPartnerSinkStatus, mCableStatus, mNumLanes, mHotPlugDetect,
+ mLinkTrainingStatus);
}
public static final @NonNull Parcelable.Creator<DisplayPortAltModeInfo> CREATOR =
@@ -195,7 +253,10 @@
int partnerSinkStatus = in.readInt();
int cableStatus = in.readInt();
int numLanes = in.readInt();
- return new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes);
+ boolean hotPlugDetect = in.readBoolean();
+ int linkTrainingStatus = in.readInt();
+ return new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes,
+ hotPlugDetect, linkTrainingStatus);
}
@Override
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 909d147..0483b71 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -37,8 +37,8 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.hardware.usb.gadget.V1_0.GadgetFunction;
-import android.hardware.usb.gadget.V1_2.UsbSpeed;
+import android.hardware.usb.gadget.GadgetFunction;
+import android.hardware.usb.gadget.UsbSpeed;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -54,15 +54,12 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Executor;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* This class allows you to access the state of USB and communicate with USB devices.
@@ -78,7 +75,7 @@
public class UsbManager {
private static final String TAG = "UsbManager";
- /**
+ /**
* Broadcast Action: A sticky broadcast for USB state change events when in device mode.
*
* This is a sticky broadcast for clients that includes USB connected/disconnected state,
@@ -104,7 +101,7 @@
* MIDI function is enabled
* </ul>
* If the sticky intent has not been found, that indicates USB is disconnected,
- * USB is not configued, MTP function is enabled, and all the other functions are disabled.
+ * USB is not configured, MTP function is enabled, and all the other functions are disabled.
*
* @hide
*/
@@ -124,7 +121,7 @@
public static final String ACTION_USB_PORT_CHANGED =
"android.hardware.usb.action.USB_PORT_CHANGED";
- /**
+ /**
* Broadcast Action: A broadcast for USB compliance warning changes.
*
* This intent is sent when a port partner's
@@ -137,7 +134,7 @@
public static final String ACTION_USB_PORT_COMPLIANCE_CHANGED =
"android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED";
- /**
+ /**
* Activity intent sent when user attaches a USB device.
*
* This intent is sent when a USB device is attached to the USB bus when in host mode.
@@ -150,7 +147,7 @@
public static final String ACTION_USB_DEVICE_ATTACHED =
"android.hardware.usb.action.USB_DEVICE_ATTACHED";
- /**
+ /**
* Broadcast Action: A broadcast for USB device detached event.
*
* This intent is sent when a USB device is detached from the USB bus when in host mode.
@@ -163,7 +160,7 @@
public static final String ACTION_USB_DEVICE_DETACHED =
"android.hardware.usb.action.USB_DEVICE_DETACHED";
- /**
+ /**
* Activity intent sent when user attaches a USB accessory.
*
* <ul>
@@ -175,7 +172,7 @@
public static final String ACTION_USB_ACCESSORY_ATTACHED =
"android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
- /**
+ /**
* Broadcast Action: A broadcast for USB accessory detached event.
*
* This intent is sent when a USB accessory is detached.
@@ -616,6 +613,7 @@
/**
* Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
+ * Must be equal to {@link GadgetFunction#NONE}
* @hide
*/
@SystemApi
@@ -623,55 +621,63 @@
/**
* Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * Must be equal to {@link GadgetFunction#MTP}
* @hide
*/
@SystemApi
- public static final long FUNCTION_MTP = GadgetFunction.MTP;
+ public static final long FUNCTION_MTP = 1 << 2;
/**
* Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * Must be equal to {@link GadgetFunction#PTP}
* @hide
*/
@SystemApi
- public static final long FUNCTION_PTP = GadgetFunction.PTP;
+ public static final long FUNCTION_PTP = 1 << 4;
/**
* Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * Must be equal to {@link GadgetFunction#RNDIS}
* @hide
*/
@SystemApi
- public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS;
+ public static final long FUNCTION_RNDIS = 1 << 5;
/**
* Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * Must be equal to {@link GadgetFunction#MIDI}
* @hide
*/
@SystemApi
- public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
+ public static final long FUNCTION_MIDI = 1 << 3;
/**
* Code for the accessory usb function.
+ * Must be equal to {@link GadgetFunction#ACCESSORY}
* @hide
*/
@SystemApi
- public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
+ public static final long FUNCTION_ACCESSORY = 1 << 1;
/**
* Code for the audio source usb function.
+ * Must be equal to {@link GadgetFunction#AUDIO_SOURCE}
* @hide
*/
@SystemApi
- public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
+ public static final long FUNCTION_AUDIO_SOURCE = 1 << 6;
/**
* Code for the adb usb function.
+ * Must be equal to {@link GadgetFunction#ADB}
* @hide
*/
@SystemApi
- public static final long FUNCTION_ADB = GadgetFunction.ADB;
+ public static final long FUNCTION_ADB = 1;
/**
* Code for the ncm source usb function.
+ * Must be equal to {@link GadgetFunction#NCM}
* @hide
*/
@SystemApi
@@ -998,7 +1004,7 @@
}
}
- /**
+ /**
* Requests temporary permission for the given package to access the device.
* This may result in a system dialog being displayed to the user
* if permission had not already been granted.
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index 8e6f6dc..268db1e 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -66,73 +66,74 @@
mSessionId = sessionId;
}
- /**
- * Subclass of {@link ResultReceiver} used by
- * {@link #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)} for providing
- * callback.
- */
- private static final class IntResultReceiver extends ResultReceiver {
- @NonNull
- private IntConsumer mConsumer;
- @NonNull
+ private abstract static class OnceResultReceiver<C> extends ResultReceiver {
+ @Nullable
+ private C mConsumer;
+ @Nullable
private Executor mExecutor;
- IntResultReceiver(@NonNull Executor executor, @NonNull IntConsumer consumer) {
+ protected OnceResultReceiver(@NonNull Executor executor, @NonNull C consumer) {
super(null);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(consumer);
mExecutor = executor;
mConsumer = consumer;
}
@Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (mExecutor != null && mConsumer != null) {
- mExecutor.execute(() -> mConsumer.accept(resultCode));
- // provide callback only once.
- clear();
+ protected final void onReceiveResult(int resultCode, Bundle resultData) {
+ final Executor executor;
+ final C consumer;
+ synchronized (this) {
+ executor = mExecutor;
+ consumer = mConsumer;
+ mExecutor = null;
+ mConsumer = null;
+ }
+ if (executor != null && consumer != null) {
+ dispatch(executor, consumer, resultCode, resultData);
}
}
- private void clear() {
- mExecutor = null;
- mConsumer = null;
+ protected abstract void dispatch(@NonNull Executor executor, @NonNull C consumer, int code,
+ Bundle data);
+ }
+
+ /**
+ * Subclass of {@link ResultReceiver} used by
+ * {@link #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)} for providing
+ * callback.
+ */
+ private static final class IntResultReceiver extends OnceResultReceiver<IntConsumer> {
+ IntResultReceiver(@NonNull Executor executor, @NonNull IntConsumer consumer) {
+ super(executor, consumer);
}
- };
+
+ @Override
+ protected void dispatch(@NonNull Executor executor, @NonNull IntConsumer consumer, int code,
+ Bundle data) {
+ executor.execute(() -> consumer.accept(code));
+ }
+ }
/**
* Subclass of {@link ResultReceiver} used by
* {@link #requestTextBoundsInfo(RectF, Executor, Consumer)} for providing
* callback.
*/
- private static final class TextBoundsInfoResultReceiver extends ResultReceiver {
- @Nullable
- private Consumer<TextBoundsInfoResult> mConsumer;
- @Nullable
- private Executor mExecutor;
-
+ private static final class TextBoundsInfoResultReceiver extends
+ OnceResultReceiver<Consumer<TextBoundsInfoResult>> {
TextBoundsInfoResultReceiver(@NonNull Executor executor,
@NonNull Consumer<TextBoundsInfoResult> consumer) {
- super(null);
- mExecutor = executor;
- mConsumer = consumer;
+ super(executor, consumer);
}
@Override
- protected void onReceiveResult(@TextBoundsInfoResult.ResultCode int resultCode,
- @Nullable Bundle resultData) {
- synchronized (this) {
- if (mExecutor != null && mConsumer != null) {
- final TextBoundsInfoResult textBoundsInfoResult = new TextBoundsInfoResult(
- resultCode, TextBoundsInfo.createFromBundle(resultData));
- mExecutor.execute(() -> mConsumer.accept(textBoundsInfoResult));
- // provide callback only once.
- clear();
- }
- }
- }
-
- private void clear() {
- mExecutor = null;
- mConsumer = null;
+ protected void dispatch(@NonNull Executor executor,
+ @NonNull Consumer<TextBoundsInfoResult> consumer, int code, Bundle data) {
+ final TextBoundsInfoResult textBoundsInfoResult = new TextBoundsInfoResult(
+ code, TextBoundsInfo.createFromBundle(data));
+ executor.execute(() -> consumer.accept(textBoundsInfoResult));
}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 083b4f6..0307c10 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2425,8 +2425,9 @@
public abstract int getHistoryTagPoolUid(int index);
/**
- * Returns a BatteryStatsHistoryIterator. Battery history will remain immutable until the
- * {@link BatteryStatsHistoryIterator#close()} method is invoked.
+ * Returns a BatteryStatsHistoryIterator. Battery history will continue being writable,
+ * but the iterator will continue iterating over the snapshot taken at the time this method
+ * is called.
*/
public abstract BatteryStatsHistoryIterator iterateBatteryStatsHistory();
@@ -5120,14 +5121,6 @@
sb.append(formatCharge(power));
}
- /**
- * Temporary for settings.
- */
- public final void dumpLocked(Context context, PrintWriter pw, String prefix, int which,
- int reqUid) {
- dumpLocked(context, pw, prefix, which, reqUid, checkWifiOnly(context));
- }
-
@SuppressWarnings("unused")
public final void dumpLocked(Context context, PrintWriter pw, String prefix, final int which,
int reqUid, boolean wifiOnly) {
@@ -7483,7 +7476,25 @@
public static final int DUMP_VERBOSE = 1<<5;
public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6;
- private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) {
+ private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) {
+ if (!checkin) {
+ synchronized (this) {
+ final long historyTotalSize = getHistoryTotalSize();
+ final long historyUsedSize = getHistoryUsedSize();
+ pw.print("Battery History (");
+ pw.print((100 * historyUsedSize) / historyTotalSize);
+ pw.print("% used, ");
+ printSizeValue(pw, historyUsedSize);
+ pw.print(" used of ");
+ printSizeValue(pw, historyTotalSize);
+ pw.print(", ");
+ pw.print(getHistoryStringPoolSize());
+ pw.print(" strings using ");
+ printSizeValue(pw, getHistoryStringPoolBytes());
+ pw.println("):");
+ }
+ }
+
final HistoryPrinter hprinter = new HistoryPrinter();
long lastTime = -1;
long baseTime = -1;
@@ -7628,27 +7639,14 @@
* @param pw a Printer to receive the dump output.
*/
@SuppressWarnings("unused")
- public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+ public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
prepareForDumpLocked();
final boolean filtering = (flags
& (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
- final long historyTotalSize = getHistoryTotalSize();
- final long historyUsedSize = getHistoryUsedSize();
- pw.print("Battery History (");
- pw.print((100 * historyUsedSize) / historyTotalSize);
- pw.print("% used, ");
- printSizeValue(pw, historyUsedSize);
- pw.print(" used of ");
- printSizeValue(pw, historyTotalSize);
- pw.print(", ");
- pw.print(getHistoryStringPoolSize());
- pw.print(" strings using ");
- printSizeValue(pw, getHistoryStringPoolBytes());
- pw.println("):");
- dumpHistoryLocked(pw, flags, histStart, false);
+ dumpHistory(pw, flags, histStart, false);
pw.println();
}
@@ -7656,6 +7654,13 @@
return;
}
+ synchronized (this) {
+ dumpLocked(context, pw, flags, reqUid, filtering);
+ }
+ }
+
+ private void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid,
+ boolean filtering) {
if (!filtering) {
SparseArray<? extends Uid> uidStats = getUidStats();
final int NU = uidStats.size();
@@ -7823,8 +7828,8 @@
}
pw.print("\"");
pw.println();
- dumpHistoryLocked(pw, flags, histStart, true);
}
+ dumpHistory(pw, flags, histStart, true);
}
if ((flags & DUMP_HISTORY_ONLY) != 0) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index ada5c55..62d9c69 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1006,12 +1006,15 @@
// been replaced with an implementation that will suspendAll and
// send VM_START.
System.out.println("Waiting for debugger first packet");
+
+ mWaiting = true;
while (!isDebuggerConnected()) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
}
}
+ mWaiting = false;
System.out.println("Debug.suspendAllAndSentVmStart");
VMDebug.suspendAllAndSendVmStart();
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index dca722e..4ce9184 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -123,6 +123,9 @@
@TestApi
public static final int MIN_SECONDARY_USER_ID = 10;
+ /** @hide */
+ public static final int MAX_SECONDARY_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+
/**
* (Arbitrary) user handle cache size.
* {@link #CACHED_USER_HANDLES} caches user handles in the range of
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 6582605..d63d87d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5170,7 +5170,7 @@
* @return true if the user shares media with its parent user, false otherwise.
*
* @deprecated use {@link #getUserProperties(UserHandle)} with
- * {@link UserProperties#getIsMediaSharedWithParent()} instead.
+ * {@link UserProperties#isMediaSharedWithParent()} instead.
* @hide
*/
@SystemApi
@@ -5183,7 +5183,7 @@
@SuppressAutoDoc
public boolean isMediaSharedWithParent() {
try {
- return getUserProperties(UserHandle.of(mUserId)).getIsMediaSharedWithParent();
+ return getUserProperties(UserHandle.of(mUserId)).isMediaSharedWithParent();
} catch (IllegalArgumentException e) {
// If the user doesn't exist, return false (for historical reasons)
return false;
@@ -5197,7 +5197,7 @@
* and will always return false for any other user type.
*
* @deprecated use {@link #getUserProperties(UserHandle)} with
- * {@link UserProperties#getIsMediaSharedWithParent()} instead.
+ * {@link UserProperties#isCredentialShareableWithParent()} instead.
* @hide
*/
@SystemApi
@@ -5210,7 +5210,7 @@
@SuppressAutoDoc
public boolean isCredentialSharableWithParent() {
try {
- return getUserProperties(UserHandle.of(mUserId)).getIsCredentialSharableWithParent();
+ return getUserProperties(UserHandle.of(mUserId)).isCredentialShareableWithParent();
} catch (IllegalArgumentException e) {
// If the user doesn't exist, return false (for historical reasons)
return false;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index a72ccad..80dd488 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -18,17 +18,10 @@
import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO;
import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
-import static android.app.AppOpsManager.OP_READ_MEDIA_VIDEO;
-import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_AUDIO;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_IMAGES;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_VIDEO;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.PER_USER_RANGE;
@@ -1869,51 +1862,14 @@
// handle obscure cases like when an app targets Q but was installed on
// a device that was originally running on P before being upgraded to Q.
- /** {@hide} */
- public boolean checkPermissionReadAudio(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_READ_MEDIA_AUDIO);
- }
-
- /** {@hide} */
- public boolean checkPermissionWriteAudio(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_WRITE_MEDIA_AUDIO);
- }
-
- /** {@hide} */
- public boolean checkPermissionReadVideo(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_READ_MEDIA_VIDEO);
- }
-
- /** {@hide} */
- public boolean checkPermissionWriteVideo(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_WRITE_MEDIA_VIDEO);
- }
-
- /** {@hide} */
+ /**
+ * @deprecated This method should not be used since it check slegacy permissions,
+ * no longer valid. Clients should check the appropriate permissions directly
+ * instead (e.g. READ_MEDIA_IMAGES).
+ *
+ * {@hide}
+ */
+ @Deprecated
public boolean checkPermissionReadImages(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
@@ -1924,17 +1880,6 @@
OP_READ_MEDIA_IMAGES);
}
- /** {@hide} */
- public boolean checkPermissionWriteImages(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_WRITE_MEDIA_IMAGES);
- }
-
private boolean checkExternalStoragePermissionAndAppOp(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId, String permission,
int op) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5b05f21..07212a2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -40,6 +40,7 @@
import android.app.NotificationManager;
import android.app.SearchManager;
import android.app.WallpaperManager;
+import android.app.tare.EconomyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -72,6 +73,7 @@
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.IBinder;
+import android.os.IpcDataCache;
import android.os.LocaleList;
import android.os.PowerManager;
import android.os.PowerManager.AutoPowerSaveModeTriggers;
@@ -124,6 +126,9 @@
/** @hide */
public static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false;
+ /** @hide default value of whether IpcDataCache is enabled or not */
+ public static final boolean IPC_DATA_CACHE_ENABLED = false;
+
// Intent actions for Settings
/**
@@ -2920,8 +2925,8 @@
public static final String AUTHORITY = "settings";
- private static final String TAG = "Settings";
- private static final boolean LOCAL_LOGV = false;
+ static final String TAG = "Settings";
+ static final boolean LOCAL_LOGV = false;
// Used in system server calling uid workaround in call()
private static boolean sInSystemServer = false;
@@ -3031,10 +3036,10 @@
}
}
- private static final class ContentProviderHolder {
+ static final class ContentProviderHolder {
private final Object mLock = new Object();
- private final Uri mUri;
+ final Uri mUri;
@GuardedBy("mLock")
@UnsupportedAppUsage
private IContentProvider mContentProvider;
@@ -3061,14 +3066,14 @@
}
// Thread-safe.
- private static class NameValueCache {
+ static class NameValueCache {
private static final boolean DEBUG = false;
- private static final String[] SELECT_VALUE_PROJECTION = new String[] {
+ static final String[] SELECT_VALUE_PROJECTION = new String[] {
Settings.NameValueTable.VALUE
};
- private static final String NAME_EQ_PLACEHOLDER = "name=?";
+ static final String NAME_EQ_PLACEHOLDER = "name=?";
// Must synchronize on 'this' to access mValues and mValuesVersion.
private final ArrayMap<String, String> mValues = new ArrayMap<>();
@@ -3089,6 +3094,14 @@
private final ArraySet<String> mAllFields;
private final ArrayMap<String, Integer> mReadableFieldsWithMaxTargetSdk;
+ private final String mSettingsType;
+
+ // Caches for settings key -> value, only for the current user
+ private final IpcDataCache<SettingsIpcDataCache.GetQuery, String> mValueCache;
+ // Cache for settings namespace -> list of settings, only for the current user
+ private final IpcDataCache<SettingsIpcDataCache.ListQuery, HashMap<String, String>>
+ mNamespaceCache;
+
@GuardedBy("this")
private GenerationTracker mGenerationTracker;
@@ -3114,6 +3127,11 @@
mReadableFieldsWithMaxTargetSdk = new ArrayMap<>();
getPublicSettingsForClass(callerClass, mAllFields, mReadableFields,
mReadableFieldsWithMaxTargetSdk);
+ mSettingsType = callerClass.getSimpleName().toLowerCase();
+ mValueCache = IPC_DATA_CACHE_ENABLED ? SettingsIpcDataCache.createValueCache(
+ mProviderHolder, mCallGetCommand, mUri, mSettingsType) : null;
+ mNamespaceCache = IPC_DATA_CACHE_ENABLED ? SettingsIpcDataCache.createListCache(
+ mProviderHolder, mCallListCommand, mSettingsType) : null;
}
public boolean putStringForUser(ContentResolver cr, String name, String value,
@@ -3212,6 +3230,30 @@
}
}
+ if (IPC_DATA_CACHE_ENABLED) {
+ if (userHandle != UserHandle.myUserId()) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "get setting for user " + userHandle
+ + " by user " + UserHandle.myUserId()
+ + " so skipping cache");
+ }
+ try {
+ return SettingsIpcDataCache.getValueFromContentProviderCall(
+ mProviderHolder, mCallGetCommand, mUri, userHandle, cr, name);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ try {
+ return mValueCache.query(new SettingsIpcDataCache.GetQuery(cr, name));
+ } catch (RuntimeException e) {
+ // Failed to query the server
+ return null;
+ }
+ }
+
+ // Fall back to old cache mechanism
final boolean isSelf = (userHandle == UserHandle.myUserId());
int currentGeneration = -1;
if (isSelf) {
@@ -3408,6 +3450,38 @@
List<String> names) {
String namespace = prefix.substring(0, prefix.length() - 1);
Config.enforceReadPermission(namespace);
+
+ if (mCallListCommand == null) {
+ // No list command specified, return empty map
+ return new ArrayMap<>();
+ }
+
+ if (IPC_DATA_CACHE_ENABLED) {
+ ArrayMap<String, String> results = new ArrayMap<>();
+ HashMap<String, String> flagsToValues;
+ try {
+ flagsToValues = mNamespaceCache.query(
+ new SettingsIpcDataCache.ListQuery(cr, prefix));
+ } catch (RuntimeException e) {
+ // Failed to query the server, return an empty map
+ return results;
+ }
+
+ if (flagsToValues != null) {
+ if (!names.isEmpty()) {
+ for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
+ if (names.contains(flag.getKey())) {
+ results.put(flag.getKey(), flag.getValue());
+ }
+ }
+ } else {
+ results.putAll(flagsToValues);
+ }
+ }
+ return results;
+ }
+
+ // Fall back to old cache mechanism
ArrayMap<String, String> keyValues = new ArrayMap<>();
int currentGeneration = -1;
@@ -3447,10 +3521,7 @@
}
}
- if (mCallListCommand == null) {
- // No list command specified, return empty map
- return keyValues;
- }
+
IContentProvider cp = mProviderHolder.getProvider(cr);
try {
@@ -3556,7 +3627,13 @@
}
}
- public void clearGenerationTrackerForTest() {
+ public void clearCachesForTest() {
+ if (IPC_DATA_CACHE_ENABLED) {
+ mValueCache.clear();
+ mNamespaceCache.clear();
+ return;
+ }
+ // Fall back to old cache mechanism
synchronized (NameValueCache.this) {
if (mGenerationTracker != null) {
mGenerationTracker.destroy();
@@ -3565,6 +3642,12 @@
mGenerationTracker = null;
}
}
+
+ public void invalidateCache() {
+ if (IPC_DATA_CACHE_ENABLED) {
+ SettingsIpcDataCache.invalidateCache(mSettingsType);
+ }
+ }
}
/**
@@ -3841,7 +3924,12 @@
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
- sNameValueCache.clearGenerationTrackerForTest();
+ sNameValueCache.clearCachesForTest();
+ }
+
+ /** @hide */
+ public static void invalidateValueCache() {
+ sNameValueCache.invalidateCache();
}
/** @hide */
@@ -6279,7 +6367,12 @@
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
- sNameValueCache.clearGenerationTrackerForTest();
+ sNameValueCache.clearCachesForTest();
+ }
+
+ /** @hide */
+ public static void invalidateValueCache() {
+ sNameValueCache.invalidateCache();
}
/** @hide */
@@ -9738,7 +9831,7 @@
/**
* Indicates whether "seen" notifications should be suppressed from the lockscreen.
* <p>
- * Type: int (0 for false, 1 for true)
+ * Type: int (0 for unset, 1 for true, 2 for false)
*
* @hide
*/
@@ -14568,15 +14661,6 @@
"app_auto_restriction_enabled";
/**
- * Feature flag to enable or disable the Forced App Standby feature.
- * Type: int (0 for false, 1 for true)
- * Default: 1
- * @hide
- */
- @Readable
- public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
-
- /**
* Whether or not to enable Forced App Standby on small battery devices.
* Type: int (0 for false, 1 for true)
* Default: 0
@@ -14599,7 +14683,7 @@
*
* @hide
*/
- public static final int DEFAULT_ENABLE_TARE = 1;
+ public static final int DEFAULT_ENABLE_TARE = EconomyManager.DEFAULT_ENABLE_TARE_MODE;
/**
* Whether to enable the TARE AlarmManager economic policy or not.
@@ -14614,7 +14698,8 @@
*
* @hide
*/
- public static final int DEFAULT_ENABLE_TARE_ALARM_MANAGER = 0;
+ public static final int DEFAULT_ENABLE_TARE_ALARM_MANAGER =
+ EconomyManager.DEFAULT_ENABLE_POLICY_ALARM ? 1 : 0;
/**
* Settings for AlarmManager's TARE EconomicPolicy (list of its economic factors).
@@ -14638,7 +14723,8 @@
*
* @hide
*/
- public static final int DEFAULT_ENABLE_TARE_JOB_SCHEDULER = 0;
+ public static final int DEFAULT_ENABLE_TARE_JOB_SCHEDULER =
+ EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER ? 1 : 0;
/**
* Settings for JobScheduler's TARE EconomicPolicy (list of its economic factors).
@@ -16424,7 +16510,12 @@
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
- sNameValueCache.clearGenerationTrackerForTest();
+ sNameValueCache.clearCachesForTest();
+ }
+
+ /** @hide */
+ public static void invalidateValueCache() {
+ sNameValueCache.invalidateCache();
}
/** @hide */
@@ -18109,6 +18200,37 @@
public static final String WRIST_ORIENTATION_MODE = "wear_wrist_orientation_mode";
/**
+ * Current lock screen state of the device
+ * (null = default value of this setting (lockscreen is never set),
+ * 0 = {@link #LOCK_SCREEN_STATE_NONE},
+ * 1 = {@link #LOCK_SCREEN_STATE_PIN},
+ * 2 = {@link #LOCK_SCREEN_STATE_PATTERN})
+ * @hide
+ */
+ public static final String LOCK_SCREEN_STATE = "lock_screen_state";
+
+ /**
+ * No lock screen set
+ * One of the possible states for {@link #LOCK_SCREEN_STATE}.
+ * @hide
+ */
+ public static final int LOCK_SCREEN_STATE_NONE = 0;
+
+ /**
+ * Lock screen set as a pin
+ * One of the possible states for {@link #LOCK_SCREEN_STATE}.
+ * @hide
+ */
+ public static final int LOCK_SCREEN_STATE_PIN = 1;
+
+ /**
+ * Lock screen set as a pattern
+ * One of the possible states for {@link #LOCK_SCREEN_STATE}.
+ * @hide
+ */
+ public static final int LOCK_SCREEN_STATE_PATTERN = 2;
+
+ /**
* Setting indicating the name of the Wear OS app package containing the device's sysui.
*
* @hide
@@ -18640,7 +18762,17 @@
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
- sNameValueCache.clearGenerationTrackerForTest();
+ sNameValueCache.clearCachesForTest();
+ }
+
+ /** @hide */
+ public static void invalidateValueCache() {
+ sNameValueCache.invalidateCache();
+ }
+
+ /** @hide */
+ public static void invalidateNamespaceCache() {
+ sNameValueCache.invalidateCache();
}
private static void handleMonitorCallback(
diff --git a/core/java/android/provider/SettingsIpcDataCache.java b/core/java/android/provider/SettingsIpcDataCache.java
new file mode 100644
index 0000000..3caf293
--- /dev/null
+++ b/core/java/android/provider/SettingsIpcDataCache.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import static android.provider.Settings.CALL_METHOD_USER_KEY;
+import static android.provider.Settings.ContentProviderHolder;
+import static android.provider.Settings.LOCAL_LOGV;
+import static android.provider.Settings.NameValueCache.NAME_EQ_PLACEHOLDER;
+import static android.provider.Settings.NameValueCache.SELECT_VALUE_PROJECTION;
+import static android.provider.Settings.TAG;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.IContentProvider;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IpcDataCache;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.util.HashMap;
+import java.util.Objects;
+
+/** @hide */
+final class SettingsIpcDataCache {
+ private static final boolean DEBUG = true;
+ private static final int NUM_MAX_ENTRIES = 2048;
+
+ static class GetQuery {
+ @NonNull final ContentResolver mContentResolver;
+ @NonNull final String mName;
+
+ GetQuery(@NonNull ContentResolver contentResolver, @NonNull String name) {
+ mContentResolver = contentResolver;
+ mName = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof GetQuery)) return false;
+ GetQuery getQuery = (GetQuery) o;
+ return mContentResolver.equals(
+ getQuery.mContentResolver) && mName.equals(getQuery.mName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mContentResolver, mName);
+ }
+ }
+
+ private static class GetQueryHandler extends IpcDataCache.QueryHandler<GetQuery, String> {
+ @NonNull final ContentProviderHolder mContentProviderHolder;
+ @NonNull final String mCallGetCommand;
+ @NonNull final Uri mUri;
+ final int mUserId;
+
+ private GetQueryHandler(
+ ContentProviderHolder contentProviderHolder, String callGetCommand, Uri uri) {
+ mContentProviderHolder = contentProviderHolder;
+ mCallGetCommand = callGetCommand;
+ mUri = uri;
+ mUserId = UserHandle.myUserId();
+ }
+
+ @Nullable
+ @Override
+ public String apply(GetQuery query) {
+ try {
+ return getValueFromContentProviderCall(mContentProviderHolder, mCallGetCommand,
+ mUri, mUserId, query);
+ } catch (RemoteException e) {
+ // Throw to prevent caching
+ e.rethrowAsRuntimeException();
+ }
+ return null;
+ }
+ }
+
+ @NonNull
+ static IpcDataCache<GetQuery, String> createValueCache(
+ @NonNull ContentProviderHolder contentProviderHolder,
+ @NonNull String callGetCommand, @NonNull Uri uri, @NonNull String type) {
+ if (DEBUG) {
+ Log.i(TAG, "Creating value cache for type:" + type);
+ }
+ IpcDataCache.Config config = new IpcDataCache.Config(
+ NUM_MAX_ENTRIES, IpcDataCache.MODULE_SYSTEM, type /* apiName */);
+ return new IpcDataCache<>(config.child("get"),
+ new GetQueryHandler(contentProviderHolder, callGetCommand, uri));
+ }
+
+ @Nullable
+ private static String getValueFromContentProviderCall(
+ @NonNull ContentProviderHolder providerHolder, @NonNull String callGetCommand,
+ @NonNull Uri uri, int userId, @NonNull GetQuery query)
+ throws RemoteException {
+ final ContentResolver cr = query.mContentResolver;
+ final String name = query.mName;
+ return getValueFromContentProviderCall(providerHolder, callGetCommand, uri, userId, cr,
+ name);
+ }
+
+ @Nullable
+ static String getValueFromContentProviderCall(
+ @NonNull ContentProviderHolder providerHolder, @NonNull String callGetCommand,
+ @NonNull Uri uri, int userId, ContentResolver cr, String name) throws RemoteException {
+ final IContentProvider cp = providerHolder.getProvider(cr);
+
+ // Try the fast path first, not using query(). If this
+ // fails (alternate Settings provider that doesn't support
+ // this interface?) then we fall back to the query/table
+ // interface.
+ if (callGetCommand != null) {
+ try {
+ Bundle args = new Bundle();
+ if (userId != UserHandle.myUserId()) {
+ args.putInt(CALL_METHOD_USER_KEY, userId);
+ }
+ Bundle b;
+ // If we're in system server and in a binder transaction we need to clear the
+ // calling uid. This works around code in system server that did not call
+ // clearCallingIdentity, previously this wasn't needed because reading settings
+ // did not do permission checking but that's no longer the case.
+ // Long term this should be removed and callers should properly call
+ // clearCallingIdentity or use a ContentResolver from the caller as needed.
+ if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ b = cp.call(cr.getAttributionSource(),
+ providerHolder.mUri.getAuthority(), callGetCommand, name,
+ args);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } else {
+ b = cp.call(cr.getAttributionSource(),
+ providerHolder.mUri.getAuthority(), callGetCommand, name, args);
+ }
+ if (b != null) {
+ return b.getString(Settings.NameValueTable.VALUE);
+ }
+ // If the response Bundle is null, we fall through
+ // to the query interface below.
+ } catch (RemoteException e) {
+ // Not supported by the remote side? Fall through
+ // to query().
+ }
+ }
+
+ Cursor c = null;
+ try {
+ Bundle queryArgs = ContentResolver.createSqlQueryBundle(
+ NAME_EQ_PLACEHOLDER, new String[]{name}, null);
+ // Same workaround as above.
+ if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ c = cp.query(cr.getAttributionSource(), uri,
+ SELECT_VALUE_PROJECTION, queryArgs, null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } else {
+ c = cp.query(cr.getAttributionSource(), uri,
+ SELECT_VALUE_PROJECTION, queryArgs, null);
+ }
+ if (c == null) {
+ Log.w(TAG, "Can't get key " + name + " from " + uri);
+ return null;
+ }
+ String value = c.moveToNext() ? c.getString(0) : null;
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "cache miss [" + uri.getLastPathSegment() + "]: "
+ + name + " = " + (value == null ? "(null)" : value));
+ }
+ return value;
+ } finally {
+ if (c != null) c.close();
+ }
+ }
+
+ static class ListQuery {
+ @NonNull ContentResolver mContentResolver;
+ @NonNull final String mPrefix;
+ ListQuery(@NonNull ContentResolver contentResolver, String prefix) {
+ mContentResolver = contentResolver;
+ mPrefix = prefix;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ListQuery)) return false;
+ ListQuery listQuery = (ListQuery) o;
+ return mContentResolver.equals(listQuery.mContentResolver) && mPrefix.equals(
+ listQuery.mPrefix);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mContentResolver, mPrefix);
+ }
+ }
+
+ private static class ListQueryHandler extends
+ IpcDataCache.QueryHandler<ListQuery, HashMap<String, String>> {
+ @NonNull final ContentProviderHolder mContentProviderHolder;
+ @NonNull final String mCallListCommand;
+
+ ListQueryHandler(@NonNull ContentProviderHolder contentProviderHolder,
+ @NonNull String callListCommand) {
+ mContentProviderHolder = contentProviderHolder;
+ mCallListCommand = callListCommand;
+ }
+
+ @Nullable
+ @Override
+ public HashMap<String, String> apply(@NonNull ListQuery query) {
+ try {
+ return getListFromContentProviderCall(query);
+ } catch (RemoteException e) {
+ // Throw to prevent caching
+ e.rethrowAsRuntimeException();
+ }
+ return null;
+ }
+
+ @Nullable
+ private HashMap<String, String> getListFromContentProviderCall(ListQuery query)
+ throws RemoteException {
+ final ContentResolver cr = query.mContentResolver;
+ final IContentProvider cp = mContentProviderHolder.getProvider(cr);
+ final String prefix = query.mPrefix;
+ final String namespace = prefix.substring(0, prefix.length() - 1);
+ HashMap<String, String> keyValues = new HashMap<>();
+
+ Bundle args = new Bundle();
+ args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
+
+ Bundle b;
+ // b/252663068: if we're in system server and the caller did not call
+ // clearCallingIdentity, the read would fail due to mismatched AttributionSources.
+ // TODO(b/256013480): remove this bypass after fixing the callers in system server.
+ if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
+ && Settings.isInSystemServer()
+ && Binder.getCallingUid() != Process.myUid()) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Fetch all flags for the namespace at once for caching purposes
+ b = cp.call(cr.getAttributionSource(),
+ mContentProviderHolder.mUri.getAuthority(), mCallListCommand, null,
+ args);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } else {
+ // Fetch all flags for the namespace at once for caching purposes
+ b = cp.call(cr.getAttributionSource(),
+ mContentProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+ }
+ if (b == null) {
+ // Invalid response, return an empty map
+ return keyValues;
+ }
+
+ // Cache all flags for the namespace
+ HashMap<String, String> flagsToValues =
+ (HashMap) b.getSerializable(Settings.NameValueTable.VALUE,
+ java.util.HashMap.class);
+ return flagsToValues;
+ }
+ }
+
+ @NonNull
+ static IpcDataCache<ListQuery, HashMap<String, String>> createListCache(
+ @NonNull ContentProviderHolder providerHolder,
+ @NonNull String callListCommand, String type) {
+ if (DEBUG) {
+ Log.i(TAG, "Creating cache for settings type:" + type);
+ }
+ IpcDataCache.Config config = new IpcDataCache.Config(
+ NUM_MAX_ENTRIES, IpcDataCache.MODULE_SYSTEM, type /* apiName */);
+ return new IpcDataCache<>(config.child("get"),
+ new ListQueryHandler(providerHolder, callListCommand));
+ }
+
+ static void invalidateCache(String type) {
+ if (DEBUG) {
+ Log.i(TAG, "Cache invalidated for type:" + type);
+ }
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, type);
+ }
+}
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index 8f49bcb..e7e44a5 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -61,6 +61,9 @@
* </service>}
* </pre>
*
+ * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
+ * separately. </p>
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index bea68b1..b4a1e8c 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -182,6 +182,11 @@
char accent = content.charAt(selStart);
int composed = event.getDeadChar(accent, i);
+ // Prevent a dead key repetition from inserting
+ if (i == composed && event.getRepeatCount() > 0) {
+ return true;
+ }
+
if (composed != 0) {
i = composed;
replace = true;
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 101a071..25ee6af 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -283,7 +283,13 @@
* A scaling factor for fonts displayed on the display. This is the same
* as {@link #density}, except that it may be adjusted in smaller
* increments at runtime based on a user preference for the font size.
+ *
+ * @deprecated this scalar factor is no longer accurate due to adaptive non-linear font scaling.
+ * Please use {@link TypedValue#applyDimension(int, float, DisplayMetrics)} or
+ * {@link TypedValue#deriveDimension(int, float, DisplayMetrics)} to convert between SP font
+ * sizes and pixels.
*/
+ @Deprecated
public float scaledDensity;
/**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 70b04a1..06fe523 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -166,6 +166,13 @@
public static final String SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS =
"settings_show_udfps_enroll_in_settings";
+ /**
+ * Flag to enable lock screen credentials transfer API in Android U.
+ * @hide
+ */
+ public static final String SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API =
+ "settings_enable_lockscreen_transfer_api";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -207,6 +214,7 @@
DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_ALERTS, "false");
DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java
index b7873b7..61f29a4 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -139,4 +139,9 @@
public T valueAt(int intIndex, int valueIndex) {
return mData.valueAt(intIndex).valueAt(valueIndex);
}
+
+ /** @return The set of values for key at position {@code intIndex}. */
+ public ArraySet<T> valuesAt(int intIndex) {
+ return mData.valueAt(intIndex);
+ }
}
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 09545fc..b93e338 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -495,6 +495,48 @@
}
/**
+ * Converts a pixel value to the given dimension, e.g. PX to DP.
+ *
+ * <p>This is just an alias of {@link #deriveDimension(int, float, DisplayMetrics)} with an
+ * easier-to-find name.
+ *
+ * @param unitToConvertTo The unit to convert to.
+ * @param pixelValue The raw pixels value to convert from.
+ * @param metrics Current display metrics to use in the conversion --
+ * supplies display density and scaling information.
+ *
+ * @return A dimension value equivalent to the given number of pixels
+ * @throws IllegalArgumentException if unitToConvertTo is not valid.
+ */
+ public static float convertPixelsToDimension(
+ @ComplexDimensionUnit int unitToConvertTo,
+ float pixelValue,
+ @NonNull DisplayMetrics metrics) {
+ return deriveDimension(unitToConvertTo, pixelValue, metrics);
+ }
+
+ /**
+ * Converts a dimension value to raw pixels, e.g. DP to PX.
+ *
+ * <p>This is just an alias of {@link #applyDimension(int, float, DisplayMetrics)} with an
+ * easier-to-find name.
+ *
+ * @param unitToConvertFrom The unit to convert from.
+ * @param value The dimension value to apply the unit to.
+ * @param metrics Current display metrics to use in the conversion --
+ * supplies display density and scaling information.
+ *
+ * @return The equivalent pixel value—i.e. the complex floating point value multiplied by the
+ * appropriate metrics depending on its unit—or zero if unit is not valid.
+ */
+ public static float convertDimensionToPixels(
+ @ComplexDimensionUnit int unitToConvertFrom,
+ float value,
+ @NonNull DisplayMetrics metrics) {
+ return applyDimension(unitToConvertFrom, value, metrics);
+ }
+
+ /**
* Return the data for this value as a dimension. Only use for values
* whose type is {@link #TYPE_DIMENSION}.
*
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 689dce8..25863a6 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2323,6 +2323,10 @@
*/
public static final float INVALID_LUMINANCE = -1;
/**
+ * Invalid HDR type value.
+ */
+ public static final int HDR_TYPE_INVALID = -1;
+ /**
* Dolby Vision high dynamic range (HDR) display.
*/
public static final int HDR_TYPE_DOLBY_VISION = 1;
@@ -2350,6 +2354,7 @@
/** @hide */
@IntDef(prefix = { "HDR_TYPE_" }, value = {
+ HDR_TYPE_INVALID,
HDR_TYPE_DOLBY_VISION,
HDR_TYPE_HDR10,
HDR_TYPE_HLG,
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9fdda29..4f3c5fe 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -22,6 +22,7 @@
import com.android.internal.policy.IShortcutService;
import android.app.IAssistDataReceiver;
+import android.content.ComponentName;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -1000,4 +1001,14 @@
* Mark a SurfaceSyncGroup stored in WindowManager as ready.
*/
oneway void markSurfaceSyncGroupReady(in IBinder syncGroupToken);
+
+ /**
+ * Invoked when a screenshot is taken of the default display to notify registered listeners.
+ *
+ * Should be invoked only by SysUI.
+ *
+ * @param displayId id of the display screenshot.
+ * @return List of ComponentNames corresponding to the activities that were notified.
+ */
+ List<ComponentName> notifyScreenshotListeners(int displayId);
}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 47da3f6..b22a43e 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -561,6 +561,7 @@
private String mKeyboardLayoutType = null;
private int mUsiVersionMajor = -1;
private int mUsiVersionMinor = -1;
+ private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
private List<MotionRange> mMotionRanges = new ArrayList<>();
/** @see InputDevice#getId() */
@@ -678,6 +679,12 @@
return this;
}
+ /** @see InputDevice#getAssociatedDisplayId() */
+ public Builder setAssociatedDisplayId(int displayId) {
+ mAssociatedDisplayId = displayId;
+ return this;
+ }
+
/** @see InputDevice#getMotionRanges() */
public Builder addMotionRange(int axis, int source,
float min, float max, float flat, float fuzz, float resolution) {
@@ -708,7 +715,7 @@
mHasBattery,
mUsiVersionMajor,
mUsiVersionMinor,
- Display.INVALID_DISPLAY);
+ mAssociatedDisplayId);
final int numRanges = mMotionRanges.size();
for (int i = 0; i < numRanges; i++) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5c23c31..fedb098 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -222,6 +222,8 @@
int transform);
private static native void nativeSetDataSpace(long transactionObj, long nativeObject,
@DataSpace.NamedDataSpace int dataSpace);
+ private static native void nativeSetExtendedRangeBrightness(long transactionObj,
+ long nativeObject, float currentBufferRatio, float desiredRatio);
private static native void nativeSetDamageRegion(long transactionObj, long nativeObject,
Region region);
private static native void nativeSetDimmingEnabled(long transactionObj, long nativeObject,
@@ -3610,6 +3612,46 @@
}
/**
+ * Sets the desired extended range brightness for the layer. This only applies for layers
+ * whose dataspace has RANGE_EXTENDED.
+ *
+ * @param sc The layer whose extended range brightness is being specified
+ * @param currentBufferRatio The current sdr/hdr ratio of the current buffer. For example
+ * if the buffer was rendered with a target SDR whitepoint of
+ * 100 nits and a max display brightness of 200 nits, this should
+ * be set to 2.0f.
+ *
+ * Default value is 1.0f.
+ *
+ * Transfer functions that encode their own brightness ranges,
+ * such as HLG or PQ, should also set this to 1.0f and instead
+ * communicate extended content brightness information via
+ * metadata such as CTA861_3 or SMPTE2086.
+ *
+ * @param desiredRatio The desired sdr/hdr ratio. This can be used to communicate the max
+ * desired brightness range. This is similar to the "max luminance"
+ * value in other HDR metadata formats, but represented as a ratio of
+ * the target SDR whitepoint to the max display brightness. The system
+ * may not be able to, or may choose not to, deliver the
+ * requested range.
+ *
+ * If unspecified, the system will attempt to provide the best range
+ * it can for the given ambient conditions & device state. However,
+ * voluntarily reducing the requested range can help improve battery
+ * life as well as can improve quality by ensuring greater bit depth
+ * is allocated to the luminance range in use.
+ * @return this
+ * @hide
+ **/
+ public @NonNull Transaction setExtendedRangeBrightness(@NonNull SurfaceControl sc,
+ float currentBufferRatio, float desiredRatio) {
+ checkPreconditions(sc);
+ nativeSetExtendedRangeBrightness(mNativeObject, sc.mNativeObject, currentBufferRatio,
+ desiredRatio);
+ return this;
+ }
+
+ /**
* Sets the trusted overlay state on this SurfaceControl and it is inherited to all the
* children. The caller must hold the ACCESS_SURFACE_FLINGER permission.
* @hide
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f6348d7..490091b 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -292,7 +292,7 @@
public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
@NonNull WindowlessWindowManager wwm) {
mWm = wwm;
- mViewRoot = new ViewRootImpl(c, d, mWm);
+ mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
addConfigCallback(c, d);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
@@ -322,7 +322,7 @@
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
- mViewRoot = new ViewRootImpl(context, display, mWm);
+ mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout());
addConfigCallback(context, display);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 508c6bd..d68e085 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7698,8 +7698,13 @@
boolean handled = false;
final ListenerInfo li = mListenerInfo;
+ boolean shouldPerformHapticFeedback = true;
if (li != null && li.mOnLongClickListener != null) {
handled = li.mOnLongClickListener.onLongClick(View.this);
+ if (handled) {
+ shouldPerformHapticFeedback =
+ li.mOnLongClickListener.onLongClickUseDefaultHapticFeedback(View.this);
+ }
}
if (!handled) {
final boolean isAnchored = !Float.isNaN(x) && !Float.isNaN(y);
@@ -7710,7 +7715,7 @@
handled = showLongClickTooltip((int) x, (int) y);
}
}
- if (handled) {
+ if (handled && shouldPerformHapticFeedback) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return handled;
@@ -25824,10 +25829,6 @@
* @param selected true if the view must be selected, false otherwise
*/
public void setSelected(boolean selected) {
- setSelected(selected, true);
- }
-
- void setSelected(boolean selected, boolean sendAccessibilityEvent) {
//noinspection DoubleNegation
if (((mPrivateFlags & PFLAG_SELECTED) != 0) != selected) {
mPrivateFlags = (mPrivateFlags & ~PFLAG_SELECTED) | (selected ? PFLAG_SELECTED : 0);
@@ -25835,13 +25836,11 @@
invalidate(true);
refreshDrawableState();
dispatchSetSelected(selected);
- if (sendAccessibilityEvent) {
- if (selected) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
- } else {
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
- }
+ if (selected) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
@@ -29922,6 +29921,19 @@
* @return true if the callback consumed the long click, false otherwise.
*/
boolean onLongClick(View v);
+
+ /**
+ * Returns whether the default {@link HapticFeedbackConstants#LONG_PRESS} haptic feedback
+ * is performed when this listener has consumed the long click. This method is called
+ * immediately after {@link #onLongClick} has returned true.
+ *
+ * @param v The view that was clicked and held.
+ * @return true to perform the default {@link HapticFeedbackConstants#LONG_PRESS} haptic
+ * feedback, or false if the handler manages all haptics itself.
+ */
+ default boolean onLongClickUseDefaultHapticFeedback(@NonNull View v) {
+ return true;
+ }
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9f5015c..0e4ac01 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4631,7 +4631,7 @@
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = 0; i < count; i++) {
- children[i].setSelected(selected, false);
+ children[i].setSelected(selected);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5165478..84ed845 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -75,7 +75,6 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -440,6 +439,7 @@
@NonNull Display mDisplay;
final DisplayManager mDisplayManager;
final String mBasePackageName;
+ final InputManager mInputManager;
final int[] mTmpLocation = new int[2];
@@ -930,13 +930,14 @@
private String mTag = TAG;
public ViewRootImpl(Context context, Display display) {
- this(context, display, WindowManagerGlobal.getWindowSession());
+ this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
}
- public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) {
+ public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
+ WindowLayout windowLayout) {
mContext = context;
mWindowSession = session;
- mWindowLayout = new WindowLayout();
+ mWindowLayout = windowLayout;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
@@ -967,6 +968,7 @@
// TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ mInputManager = context.getSystemService(InputManager.class);
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
mHandwritingInitiator = new HandwritingInitiator(
mViewConfiguration,
@@ -5336,7 +5338,7 @@
Log.e(mTag, "No input channel to request Pointer Capture.");
return;
}
- InputManager.getInstance().requestPointerCapture(inputToken, enabled);
+ mInputManager.requestPointerCapture(inputToken, enabled);
}
private void handlePointerCaptureChanged(boolean hasCapture) {
@@ -6859,9 +6861,8 @@
if (event.getPointerCount() != 1) {
return;
}
- final boolean needsStylusPointerIcon =
- InputManager.getInstance().isStylusPointerIconEnabled()
- && event.isStylusPointer();
+ final boolean needsStylusPointerIcon = event.isStylusPointer()
+ && mInputManager.isStylusPointerIconEnabled();
if (needsStylusPointerIcon || event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
|| event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
@@ -6933,7 +6934,7 @@
PointerIcon pointerIcon = null;
- if (event.isStylusPointer() && InputManager.getInstance().isStylusPointerIconEnabled()) {
+ if (event.isStylusPointer() && mInputManager.isStylusPointerIconEnabled()) {
pointerIcon = mHandwritingInitiator.onResolvePointerIcon(mContext, event);
}
@@ -6948,14 +6949,14 @@
mPointerIconType = pointerType;
mCustomPointerIcon = null;
if (mPointerIconType != PointerIcon.TYPE_CUSTOM) {
- InputManager.getInstance().setPointerIconType(pointerType);
+ mInputManager.setPointerIconType(pointerType);
return true;
}
}
if (mPointerIconType == PointerIcon.TYPE_CUSTOM &&
!pointerIcon.equals(mCustomPointerIcon)) {
mCustomPointerIcon = pointerIcon;
- InputManager.getInstance().setCustomPointerIcon(mCustomPointerIcon);
+ mInputManager.setCustomPointerIcon(mCustomPointerIcon);
}
return true;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e437c1f..40898d0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -94,6 +94,7 @@
import android.app.Presentation;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -628,6 +629,114 @@
@interface DisplayImePolicy {}
/**
+ * The root state of all the state. This is an abstract state, and the keyguard should be in
+ * one of this sub state.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_ROOT = 0x0;
+
+ /**
+ * Keyguard is off, so activity can be shown on the screen.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_OFF = 0x1;
+
+ /**
+ * The keyguard is off, but lock screen is still rendered on the screen. Waiting for
+ * starting unlock animation.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_GOING_AWAY = 0x11;
+
+ /**
+ * They keyguard is on, so normal activities cannot be shown on the screen. This is an abstract
+ * state, and the keyguard should be in one of ths sub state.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_ON = 0x2;
+
+ /**
+ * The keyguard is on and not occluded.
+ * @hide
+ */
+ int KEYGUARD_STATE_KEYGUARD_TOP = 0x21;
+
+ /**
+ * The keyguard is on, and the lock screen is shown.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_LOCKSCREEN_SHOWN = 0x211;
+
+ /**
+ * The keyguard is on, and the AOD is shown.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_AOD_SHOWN = 0x212;
+
+ /**
+ * The keyguard is on but it's occluded by a normal SHOW_WHEN_LOCKED activity (i.e. non
+ * occluded by Dream activity).
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_OCCLUDED = 0x22;
+
+ /**
+ * The keyguard is on but it's occluded by a Dream activity.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_DREAMING = 0x221;
+
+ /** @hide */
+ @IntDef(prefix = { "KEYGUARD_STATE_" }, value = {
+ KEYGUARD_STATE_ROOT,
+ KEYGUARD_STATE_OFF,
+ KEYGUARD_STATE_GOING_AWAY,
+ KEYGUARD_STATE_ON,
+ KEYGUARD_STATE_KEYGUARD_TOP,
+ KEYGUARD_STATE_LOCKSCREEN_SHOWN,
+ KEYGUARD_STATE_AOD_SHOWN,
+ KEYGUARD_STATE_OCCLUDED,
+ KEYGUARD_STATE_DREAMING,
+ })
+ @interface KeyguardState {}
+
+ /**
+ * @hide
+ */
+ static String keyguardStateToString(@KeyguardState int type) {
+ switch (type) {
+ case KEYGUARD_STATE_ROOT:
+ return "ROOT";
+ case KEYGUARD_STATE_OFF:
+ return "KEYGUARD_OFF";
+ case KEYGUARD_STATE_GOING_AWAY:
+ return "KEYGUARD_GOING_AWAY";
+ case KEYGUARD_STATE_ON:
+ return "KEYGUARD_ON";
+ case KEYGUARD_STATE_KEYGUARD_TOP:
+ return "KEYGUARD_TOP";
+ case KEYGUARD_STATE_LOCKSCREEN_SHOWN:
+ return "KEYGUARD_LOCKSCREEN_SHOWN";
+ case KEYGUARD_STATE_AOD_SHOWN:
+ return "KEYGUARD_AOD_SHOWN";
+ case KEYGUARD_STATE_OCCLUDED:
+ return "KEYGUARD_OCCLUDED";
+ case KEYGUARD_STATE_DREAMING:
+ return "KEYGUARD_DREAMING";
+ default:
+ return "KEYGUARD_STATE_UNKNOWN(" + Integer.toHexString(type) + ")";
+ }
+ }
+
+ /**
* Exception that is thrown when trying to add view whose
* {@link LayoutParams} {@link LayoutParams#token}
* is invalid.
@@ -990,6 +1099,77 @@
"android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
/**
+ * Activity level {@link android.content.pm.PackageManager.Property PackageManager
+ * .Property} for an app to inform the system that the activity should be excluded from the
+ * compatibility override for orientation set by the device manufacturer.
+ *
+ * <p>With this property set to {@code true} or unset, device manufacturers can override
+ * orientation for the activity using their discretion to improve display compatibility.
+ *
+ * <p>With this property set to {@code false}, device manufactured per-app override for
+ * orientation won't be applied.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <activity>
+ * <property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"
+ * android:value="true|false"/>
+ * </activity>
+ * </pre>
+ *
+ * @hide
+ */
+ // TODO(b/263984287): Make this public API.
+ String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE =
+ "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";
+
+ /**
+ * Activity level {@link android.content.pm.PackageManager.Property PackageManager
+ * .Property} for an app to inform the system that the activity should be opted-out from the
+ * compatibility override that fixes display orientation to landscape natural orientation when
+ * an activity is fullscreen.
+ *
+ * <p>When this compat override is enabled and while display is fixed to the landscape natural
+ * orientation, the orientation requested by the activity will be still respected by bounds
+ * resolution logic. For instance, if an activity requests portrait orientation, then activity
+ * will appear in the letterbox mode for fixed orientation with the display rotated to the
+ * lanscape natural orientation.
+ *
+ * <p>The treatment is disabled by default but device manufacturers can enable the treatment
+ * using their discretion to improve display compatibility on the displays that have
+ * ignoreOrientationRequest display setting enabled (enables compatibility mode for fixed
+ * orientation, see <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced letterboxing</a>
+ * for more details).
+ *
+ * <p>With this property set to {@code true} or unset, the system wiil use landscape display
+ * orientation when the following conditions are met:
+ * <ul>
+ * <li>Natural orientation of the display is landscape
+ * <li>ignoreOrientationRequest display setting is enabled
+ * <li>Activity is fullscreen.
+ * <li>Device manufacturer enabled the treatment.
+ * </ul>
+ *
+ * <p>With this property set to {@code false}, device manufactured per-app override for
+ * display orientation won't be applied.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <activity>
+ * <property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE"
+ * android:value="true|false"/>
+ * </activity>
+ * </pre>
+ *
+ * @hide
+ */
+ // TODO(b/263984287): Make this public API.
+ String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE =
+ "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE";
+
+ /**
* @hide
*/
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
@@ -5269,4 +5449,18 @@
default Bitmap snapshotTaskForRecents(@IntRange(from = 0) int taskId) {
return null;
}
+
+ /**
+ * Invoked when a screenshot is taken of the default display to notify registered listeners.
+ *
+ * Should be invoked only by SysUI.
+ *
+ * @param displayId id of the display screenshot.
+ * @return List of ComponentNames corresponding to the activities that were notified.
+ * @hide
+ */
+ @SystemApi
+ default @NonNull List<ComponentName> notifyScreenshotListeners(int displayId) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index acc0c0b..4a9dc5b 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -388,7 +388,8 @@
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
- root = new ViewRootImpl(view.getContext(), display, windowlessSession);
+ root = new ViewRootImpl(view.getContext(), display,
+ windowlessSession, new WindowlessWindowLayout());
}
view.setLayoutParams(wparams);
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 19d49d9..7ea8c0c 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.annotation.UiContext;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Region;
@@ -412,4 +413,15 @@
IBinder getDefaultToken() {
return mDefaultToken;
}
+
+ @Override
+ @NonNull
+ public List<ComponentName> notifyScreenshotListeners(int displayId) {
+ try {
+ return List.copyOf(WindowManagerGlobal.getWindowManagerService()
+ .notifyScreenshotListeners(displayId));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 141849f..b74b80e 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -20,6 +20,8 @@
import android.graphics.Point;
import android.graphics.Rect;
+import java.util.function.Supplier;
+
/**
* Metrics about a Window, consisting of the bounds and {@link WindowInsets}.
* <p>
@@ -50,8 +52,9 @@
public final class WindowMetrics {
@NonNull
private final Rect mBounds;
- @NonNull
- private final WindowInsets mWindowInsets;
+
+ private WindowInsets mWindowInsets;
+ private Supplier<WindowInsets> mWindowInsetsSupplier;
/** @see android.util.DisplayMetrics#density */
private final float mDensity;
@@ -81,6 +84,21 @@
}
/**
+ * Similar to {@link #WindowMetrics(Rect, WindowInsets, float)} but the window insets are
+ * computed when {@link #getWindowInsets()} is first time called. This reduces unnecessary
+ * calculation and the overhead of obtaining insets state from server side because most
+ * callers are usually only interested in {@link #getBounds()}.
+ *
+ * @hide
+ */
+ public WindowMetrics(@NonNull Rect bounds, @NonNull Supplier<WindowInsets> windowInsetsSupplier,
+ float density) {
+ mBounds = bounds;
+ mWindowInsetsSupplier = windowInsetsSupplier;
+ mDensity = density;
+ }
+
+ /**
* Returns the bounds of the area associated with this window or
* {@link android.annotation.UiContext}.
* <p>
@@ -121,7 +139,10 @@
*/
@NonNull
public WindowInsets getWindowInsets() {
- return mWindowInsets;
+ if (mWindowInsets != null) {
+ return mWindowInsets;
+ }
+ return mWindowInsets = mWindowInsetsSupplier.get();
}
/**
diff --git a/core/java/android/view/WindowlessWindowLayout.java b/core/java/android/view/WindowlessWindowLayout.java
new file mode 100644
index 0000000..e2afaa5
--- /dev/null
+++ b/core/java/android/view/WindowlessWindowLayout.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.app.WindowConfiguration.WindowingMode;
+import android.graphics.Rect;
+import android.view.WindowInsets.Type.InsetsType;
+import android.window.ClientWindowFrames;
+
+/**
+ * Computes window frames for the windowless window.
+ *
+ * This can't be replaced with the regular WindowLayout because WindowLayout computes bounds
+ * with insets and cutout values. Since windowless windows aren't affected by insets and
+ * instead are bound by their parent, it will compute incorrect bounds for them if insets are used.
+ *
+ * @hide
+ */
+public class WindowlessWindowLayout extends WindowLayout {
+
+ @Override
+ public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
+ Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
+ int requestedWidth, int requestedHeight, @InsetsType int requestedVisibleTypes,
+ float compatScale, ClientWindowFrames frames) {
+ if (frames.attachedFrame == null) {
+ frames.frame.set(0, 0, attrs.width, attrs.height);
+ frames.parentFrame.set(frames.frame);
+ frames.displayFrame.set(frames.frame);
+ return;
+ }
+
+ final int height = calculateLength(attrs.height, requestedHeight,
+ frames.attachedFrame.height());
+ final int width = calculateLength(attrs.width, requestedWidth,
+ frames.attachedFrame.width());
+ Gravity.apply(attrs.gravity, width, height, frames.attachedFrame,
+ (int) (attrs.x + attrs.horizontalMargin),
+ (int) (attrs.y + attrs.verticalMargin),
+ frames.frame);
+ frames.displayFrame.set(frames.frame);
+ frames.parentFrame.set(frames.attachedFrame);
+ }
+
+ private static int calculateLength(int attrLength, int requestedLength, int parentLength) {
+ if (attrLength == MATCH_PARENT) {
+ return parentLength;
+ }
+ if (attrLength == WRAP_CONTENT) {
+ return requestedLength;
+ }
+ return attrLength;
+ }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7d37c50..b157ea0 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -94,10 +94,7 @@
private InsetsState mInsetsState;
private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
private final MergedConfiguration mTmpConfig = new MergedConfiguration();
- private final InsetsState mTmpInsetsState = new InsetsState();
- private final Rect mTmpDisplayCutoutSafe = new Rect();
- private final Rect mTmpWindowBounds = new Rect();
- private final WindowLayout mLayout = new WindowLayout();
+ private final WindowlessWindowLayout mLayout = new WindowlessWindowLayout();
public WindowlessWindowManager(Configuration c, SurfaceControl rootSurface,
IBinder hostInputToken) {
@@ -349,27 +346,21 @@
}
WindowManager.LayoutParams attrs = state.mParams;
- mTmpFrames.attachedFrame = state.mAttachedFrame;
+ ClientWindowFrames frames = new ClientWindowFrames();
+ frames.attachedFrame = state.mAttachedFrame;
- if (state.mAttachedFrame == null) {
- mTmpWindowBounds.set(0, 0, requestedWidth, requestedHeight);
- } else {
- mTmpWindowBounds.set(state.mAttachedFrame);
- }
+ mLayout.computeFrames(attrs, null, null, null, WindowConfiguration.WINDOWING_MODE_UNDEFINED,
+ requestedWidth, requestedHeight, 0, 0,
+ frames);
- mLayout.computeFrames(attrs, mTmpInsetsState, mTmpDisplayCutoutSafe, mTmpWindowBounds,
- WindowConfiguration.WINDOWING_MODE_UNDEFINED, requestedWidth, requestedHeight, 0,
- 1f, mTmpFrames);
-
- state.mFrame.set(mTmpFrames.frame);
+ state.mFrame.set(frames.frame);
if (outFrames != null) {
- outFrames.frame.set(mTmpFrames.frame);
- outFrames.parentFrame.set(mTmpFrames.parentFrame);
- outFrames.displayFrame.set(mTmpFrames.displayFrame);
+ outFrames.frame.set(frames.frame);
+ outFrames.parentFrame.set(frames.parentFrame);
+ outFrames.displayFrame.set(frames.displayFrame);
}
- t.setPosition(leash, mTmpFrames.frame.left, mTmpFrames.frame.top);
- t.setWindowCrop(leash, mTmpFrames.frame.width(), mTmpFrames.frame.height());
+ t.setPosition(leash, frames.frame.left, frames.frame.top);
if (viewFlags == View.VISIBLE) {
// TODO(b/262892794) ViewRootImpl modifies the app's rendering SurfaceControl
diff --git a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
index dd320e1..12e0814 100644
--- a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
+++ b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
@@ -22,6 +22,7 @@
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.MagnificationConfig;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -240,6 +241,27 @@
}
/**
+ * Sets the strokeWidth and color of the accessibility focus rectangle.
+ *
+ * @param strokeWidth The stroke width of the rectangle in pixels.
+ * Setting this value to zero results in no focus rectangle being drawn.
+ * @param color The color of the rectangle.
+ */
+ public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) {
+ IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
+ if (connection != null) {
+ try {
+ connection.setFocusAppearance(strokeWidth, color);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting the strokeWidth and color of the "
+ + "accessibility focus rectangle", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* An IAccessibilityServiceClient that handles interrupts, accessibility events, and system
* connection.
*/
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 8e335e8..3ce419e 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -230,6 +230,17 @@
*/
public static final int FLAG_CONTENT_CONTROLS = 4;
+
+ /**
+ * {@link ComponentName} for the Accessibility Menu {@link AccessibilityService} as provided
+ * inside the system build, used for automatic migration to this version of the service.
+ * @hide
+ */
+ public static final ComponentName ACCESSIBILITY_MENU_IN_SYSTEM =
+ new ComponentName("com.android.systemui.accessibility.accessibilitymenu",
+ "com.android.systemui.accessibility.accessibilitymenu"
+ + ".AccessibilityMenuService");
+
@UnsupportedAppUsage
static final Object sInstanceSync = new Object();
diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java
index 203d79a..c19abf9 100644
--- a/core/java/android/window/TaskFragmentCreationParams.java
+++ b/core/java/android/window/TaskFragmentCreationParams.java
@@ -52,10 +52,26 @@
@NonNull
private final IBinder mOwnerToken;
- /** The initial bounds of the TaskFragment. Fills parent if empty. */
+ /**
+ * The initial bounds of the TaskFragment. Fills parent if empty.
+ * TODO(b/232476698): cleanup with update CTS.
+ */
@NonNull
private final Rect mInitialBounds = new Rect();
+ /**
+ * The initial relative bounds of the TaskFragment in parent coordinate.
+ * Fills parent if empty.
+ */
+ @NonNull
+ private final Rect mInitialRelativeBounds = new Rect();
+
+ /**
+ * Whether the params are using {@link Builder#setInitialRelativeBounds(Rect)}.
+ * TODO(b/232476698): remove after remove mInitialBounds
+ */
+ private final boolean mAreInitialRelativeBoundsSet;
+
/** The initial windowing mode of the TaskFragment. Inherits from parent if not set. */
@WindowingMode
private final int mWindowingMode;
@@ -94,6 +110,7 @@
private TaskFragmentCreationParams(
@NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken,
@NonNull IBinder ownerToken, @NonNull Rect initialBounds,
+ @NonNull Rect initialRelativeBounds, boolean areInitialRelativeBoundsSet,
@WindowingMode int windowingMode, @Nullable IBinder pairedPrimaryFragmentToken,
@Nullable IBinder pairedActivityToken) {
if (pairedPrimaryFragmentToken != null && pairedActivityToken != null) {
@@ -104,6 +121,8 @@
mFragmentToken = fragmentToken;
mOwnerToken = ownerToken;
mInitialBounds.set(initialBounds);
+ mInitialRelativeBounds.set(initialRelativeBounds);
+ mAreInitialRelativeBoundsSet = areInitialRelativeBoundsSet;
mWindowingMode = windowingMode;
mPairedPrimaryFragmentToken = pairedPrimaryFragmentToken;
mPairedActivityToken = pairedActivityToken;
@@ -129,6 +148,23 @@
return mInitialBounds;
}
+ /**
+ * TODO(b/232476698): remove the hide with adding CTS for this in next release.
+ * @hide
+ */
+ @NonNull
+ public Rect getInitialRelativeBounds() {
+ return mInitialRelativeBounds;
+ }
+
+ /**
+ * TODO(b/232476698): remove after remove mInitialBounds.
+ * @hide
+ */
+ public boolean areInitialRelativeBoundsSet() {
+ return mAreInitialRelativeBoundsSet;
+ }
+
@WindowingMode
public int getWindowingMode() {
return mWindowingMode;
@@ -157,6 +193,8 @@
mFragmentToken = in.readStrongBinder();
mOwnerToken = in.readStrongBinder();
mInitialBounds.readFromParcel(in);
+ mInitialRelativeBounds.readFromParcel(in);
+ mAreInitialRelativeBoundsSet = in.readBoolean();
mWindowingMode = in.readInt();
mPairedPrimaryFragmentToken = in.readStrongBinder();
mPairedActivityToken = in.readStrongBinder();
@@ -169,6 +207,8 @@
dest.writeStrongBinder(mFragmentToken);
dest.writeStrongBinder(mOwnerToken);
mInitialBounds.writeToParcel(dest, flags);
+ mInitialRelativeBounds.writeToParcel(dest, flags);
+ dest.writeBoolean(mAreInitialRelativeBoundsSet);
dest.writeInt(mWindowingMode);
dest.writeStrongBinder(mPairedPrimaryFragmentToken);
dest.writeStrongBinder(mPairedActivityToken);
@@ -195,6 +235,7 @@
+ " fragmentToken=" + mFragmentToken
+ " ownerToken=" + mOwnerToken
+ " initialBounds=" + mInitialBounds
+ + " initialRelativeBounds=" + mInitialRelativeBounds
+ " windowingMode=" + mWindowingMode
+ " pairedFragmentToken=" + mPairedPrimaryFragmentToken
+ " pairedActivityToken=" + mPairedActivityToken
@@ -222,6 +263,11 @@
@NonNull
private final Rect mInitialBounds = new Rect();
+ @NonNull
+ private final Rect mInitialRelativeBounds = new Rect();
+
+ private boolean mAreInitialRelativeBoundsSet;
+
@WindowingMode
private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -245,6 +291,19 @@
return this;
}
+ /**
+ * Sets the initial relative bounds for the TaskFragment in parent coordinate.
+ * Set to empty to fill parent.
+ * TODO(b/232476698): remove the hide with adding CTS for this in next release.
+ * @hide
+ */
+ @NonNull
+ public Builder setInitialRelativeBounds(@NonNull Rect bounds) {
+ mInitialRelativeBounds.set(bounds);
+ mAreInitialRelativeBoundsSet = true;
+ return this;
+ }
+
/** Sets the initial windowing mode for the TaskFragment. */
@NonNull
public Builder setWindowingMode(@WindowingMode int windowingMode) {
@@ -296,8 +355,8 @@
@NonNull
public TaskFragmentCreationParams build() {
return new TaskFragmentCreationParams(mOrganizer, mFragmentToken, mOwnerToken,
- mInitialBounds, mWindowingMode, mPairedPrimaryFragmentToken,
- mPairedActivityToken);
+ mInitialBounds, mInitialRelativeBounds, mAreInitialRelativeBoundsSet,
+ mWindowingMode, mPairedPrimaryFragmentToken, mPairedActivityToken);
}
}
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index cc48d46..a2fa162 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -306,6 +306,26 @@
}
/**
+ * Resizes a container by providing a bounds in its parent coordinate.
+ * This is only used by {@link TaskFragmentOrganizer}.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setRelativeBounds(
+ @NonNull WindowContainerToken container, @NonNull Rect relBounds) {
+ Change chg = getOrCreateChange(container.asBinder());
+ if (chg.mRelativeBounds == null) {
+ chg.mRelativeBounds = new Rect();
+ }
+ chg.mRelativeBounds.set(relBounds);
+ chg.mChangeMask |= Change.CHANGE_RELATIVE_BOUNDS;
+ // Bounds will be overridden.
+ chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+ return this;
+ }
+
+ /**
* Reparents a container into another one. The effect of a {@code null} parent can vary. For
* example, reparenting a stack to {@code null} will reparent it to its display.
*
@@ -979,6 +999,7 @@
public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
public static final int CHANGE_DRAG_RESIZING = 1 << 8;
+ public static final int CHANGE_RELATIVE_BOUNDS = 1 << 9;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
@@ -994,6 +1015,8 @@
private Rect mPinnedBounds = null;
private SurfaceControl.Transaction mBoundsChangeTransaction = null;
private Rect mBoundsChangeSurfaceBounds = null;
+ @Nullable
+ private Rect mRelativeBounds = null;
private int mActivityWindowingMode = -1;
private int mWindowingMode = -1;
@@ -1022,6 +1045,10 @@
mBoundsChangeSurfaceBounds = new Rect();
mBoundsChangeSurfaceBounds.readFromParcel(in);
}
+ if ((mChangeMask & Change.CHANGE_RELATIVE_BOUNDS) != 0) {
+ mRelativeBounds = new Rect();
+ mRelativeBounds.readFromParcel(in);
+ }
mWindowingMode = in.readInt();
mActivityWindowingMode = in.readInt();
@@ -1069,6 +1096,11 @@
mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds
: new Rect(other.mBoundsChangeSurfaceBounds);
}
+ if (other.mRelativeBounds != null) {
+ mRelativeBounds = transfer
+ ? other.mRelativeBounds
+ : new Rect(other.mRelativeBounds);
+ }
}
public int getWindowingMode() {
@@ -1156,6 +1188,11 @@
return mBoundsChangeSurfaceBounds;
}
+ @Nullable
+ public Rect getRelativeBounds() {
+ return mRelativeBounds;
+ }
+
@Override
public String toString() {
final boolean changesBounds =
@@ -1196,6 +1233,9 @@
if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
sb.append("ignoreOrientationRequest:" + mIgnoreOrientationRequest + ",");
}
+ if ((mChangeMask & CHANGE_RELATIVE_BOUNDS) != 0) {
+ sb.append("relativeBounds:").append(mRelativeBounds).append(",");
+ }
sb.append("}");
return sb.toString();
}
@@ -1221,6 +1261,9 @@
if (mBoundsChangeSurfaceBounds != null) {
mBoundsChangeSurfaceBounds.writeToParcel(dest, flags);
}
+ if (mRelativeBounds != null) {
+ mRelativeBounds.writeToParcel(dest, flags);
+ }
dest.writeInt(mWindowingMode);
dest.writeInt(mActivityWindowingMode);
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 06449d5..11bd47d 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -41,6 +41,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
/**
* A controller to handle {@link android.view.WindowMetrics} related APIs, which are
@@ -53,6 +54,9 @@
* @hide
*/
public final class WindowMetricsController {
+ // TODO(b/151908239): Remove and always enable this if it is stable.
+ private static final boolean LAZY_WINDOW_INSETS = android.os.SystemProperties.getBoolean(
+ "persist.wm.debug.win_metrics_lazy_insets", false);
private final Context mContext;
public WindowMetricsController(@NonNull Context context) {
@@ -92,16 +96,11 @@
windowingMode = winConfig.getWindowingMode();
}
final IBinder token = Context.getToken(mContext);
- final WindowInsets windowInsets = getWindowInsetsFromServerForCurrentDisplay(token,
- bounds, isScreenRound, windowingMode);
- return new WindowMetrics(bounds, windowInsets, density);
- }
-
- private WindowInsets getWindowInsetsFromServerForCurrentDisplay(
- IBinder token, Rect bounds, boolean isScreenRound,
- @WindowConfiguration.WindowingMode int windowingMode) {
- return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), token, bounds,
- isScreenRound, windowingMode);
+ final Supplier<WindowInsets> insetsSupplier = () -> getWindowInsetsFromServerForDisplay(
+ mContext.getDisplayId(), token, bounds, isScreenRound, windowingMode);
+ return LAZY_WINDOW_INSETS
+ ? new WindowMetrics(new Rect(bounds), insetsSupplier, density)
+ : new WindowMetrics(new Rect(bounds), insetsSupplier.get(), density);
}
/**
diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
index e3cc4f1..d0b5811 100644
--- a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
+++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
@@ -47,7 +47,9 @@
/* num_app_provided_app_targets = 6 */ appProvidedApp,
/* is_workprofile = 7 */ isWorkprofile,
/* previewType = 8 */ typeFromPreviewInt(previewType),
- /* intentType = 9 */ typeFromIntentString(intent));
+ /* intentType = 9 */ typeFromIntentString(intent),
+ /* num_provided_custom_actions = 10 */ 0,
+ /* reselection_action_provided = 11 */ false);
}
@Override
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index f56f818..7beb059 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -163,6 +163,9 @@
@Override
@Nullable
public ChooserListAdapter getWorkListAdapter() {
+ if (getCount() == 1) {
+ return null;
+ }
return getAdapterForIndex(PROFILE_WORK).getListAdapter();
}
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 1ecaf21..7677912 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -142,6 +142,9 @@
@Override
@Nullable
public ResolverListAdapter getWorkListAdapter() {
+ if (getCount() == 1) {
+ return null;
+ }
return getAdapterForIndex(PROFILE_WORK);
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 4f7f8ba..b9373be 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -567,11 +567,6 @@
public static final String VOLUME_SEPARATE_NOTIFICATION = "volume_separate_notification";
/**
- * (boolean) Whether the clipboard overlay is enabled.
- */
- public static final String CLIPBOARD_OVERLAY_ENABLED = "clipboard_overlay_enabled";
-
- /**
* (boolean) Whether widget provider info would be saved to / loaded from system persistence
* layer as opposed to individual manifests in respective apps.
*/
diff --git a/core/java/com/android/internal/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java
index 7571073..cc37c69 100644
--- a/core/java/com/android/internal/expresslog/Counter.java
+++ b/core/java/com/android/internal/expresslog/Counter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,9 +39,7 @@
* @hide
*/
public static void logIncrement(@NonNull String metricId, long amount) {
- final long metricIdHash = hashString(metricId);
+ final long metricIdHash = Utils.hashString(metricId);
FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount);
}
-
- private static native long hashString(String stringToHash);
}
diff --git a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl b/core/java/com/android/internal/expresslog/Utils.java
similarity index 65%
rename from core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
rename to core/java/com/android/internal/expresslog/Utils.java
index 124a319..d82192f 100644
--- a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
+++ b/core/java/com/android/internal/expresslog/Utils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-package android.credentials;
+package com.android.internal.expresslog;
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IRegisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+final class Utils {
+ static native long hashString(String stringToHash);
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 04b7239cb..9e176e3 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -40,12 +40,12 @@
import android.util.SparseArray;
import android.util.TimeUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ParseUtils;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -115,9 +115,11 @@
static final int STATE_BATTERY_HEALTH_SHIFT = 26;
static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
static final int STATE_BATTERY_PLUG_SHIFT = 24;
+
// We use the low bit of the battery state int to indicate that we have full details
// from a battery level change.
- static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
+ static final int BATTERY_LEVEL_DETAILS_FLAG = 0x00000001;
+
// Flag in history tag index: indicates that this is the first occurrence of this tag,
// therefore the tag value is written in the parcel
static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
@@ -196,6 +198,8 @@
private final VarintParceler mVarintParceler = new VarintParceler();
private byte mLastHistoryStepLevel = 0;
private boolean mMutable = true;
+ private final BatteryStatsHistory mWritableHistory;
+ private boolean mCleanupEnabled = true;
/**
* A delegate responsible for computing additional details for a step in battery history.
@@ -270,10 +274,32 @@
initHistoryBuffer();
}
+ /**
+ * Creates a read-only wrapper for the supplied writable history.
+ */
+ public BatteryStatsHistory(BatteryStatsHistory writableHistory) {
+ this(Parcel.obtain(), writableHistory.mSystemDir, 0, 0, null, null, null, writableHistory);
+ mMutable = false;
+
+ synchronized (mWritableHistory) {
+ // Make a copy of battery history to avoid concurrent modification.
+ mHistoryBuffer.appendFrom(mWritableHistory.mHistoryBuffer, 0,
+ mWritableHistory.mHistoryBuffer.dataSize());
+ }
+ }
+
@VisibleForTesting
public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
int maxHistoryFiles, int maxHistoryBufferSize,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) {
+ this(historyBuffer, systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator,
+ clock, tracer, null);
+ }
+
+ private BatteryStatsHistory(Parcel historyBuffer, File systemDir,
+ int maxHistoryFiles, int maxHistoryBufferSize,
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer,
+ BatteryStatsHistory writableHistory) {
mHistoryBuffer = historyBuffer;
mSystemDir = systemDir;
mMaxHistoryFiles = maxHistoryFiles;
@@ -281,6 +307,7 @@
mStepDetailsCalculator = stepDetailsCalculator;
mTracer = tracer;
mClock = clock;
+ mWritableHistory = writableHistory;
mHistoryDir = new File(systemDir, HISTORY_DIR);
mHistoryDir.mkdirs();
@@ -290,21 +317,17 @@
final Set<Integer> dedup = new ArraySet<>();
// scan directory, fill mFileNumbers and mActiveFile.
- mHistoryDir.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- final int b = name.lastIndexOf(FILE_SUFFIX);
- if (b <= 0) {
- return false;
- }
- final Integer c =
- ParseUtils.parseInt(name.substring(0, b), -1);
- if (c != -1) {
- dedup.add(c);
- return true;
- } else {
- return false;
- }
+ mHistoryDir.listFiles((dir, name) -> {
+ final int b = name.lastIndexOf(FILE_SUFFIX);
+ if (b <= 0) {
+ return false;
+ }
+ final int c = ParseUtils.parseInt(name.substring(0, b), -1);
+ if (c != -1) {
+ dedup.add(c);
+ return true;
+ } else {
+ return false;
}
});
if (!dedup.isEmpty()) {
@@ -326,21 +349,29 @@
mHistoryBuffer = Parcel.obtain();
mSystemDir = null;
mHistoryDir = null;
+ mWritableHistory = null;
initHistoryBuffer();
}
/**
- * Used when BatteryStatsImpl object is created from deserialization of a parcel,
- * such as a checkin file.
+ * Used when BatteryStatsHistory object is created from deserialization of a BatteryUsageStats
+ * parcel.
*/
- private BatteryStatsHistory(Parcel historyBuffer,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
- mHistoryBuffer = historyBuffer;
- mTracer = new TraceDelegate();
- mClock = clock;
+ private BatteryStatsHistory(Parcel parcel) {
+ mClock = Clock.SYSTEM_CLOCK;
+ mTracer = null;
mSystemDir = null;
mHistoryDir = null;
- mStepDetailsCalculator = stepDetailsCalculator;
+ mStepDetailsCalculator = null;
+ mWritableHistory = null;
+ mMutable = false;
+
+ final byte[] historyBlob = parcel.readBlob();
+
+ mHistoryBuffer = Parcel.obtain();
+ mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
+
+ readFromParcel(parcel, true /* useBlobs */);
}
private void initHistoryBuffer() {
@@ -384,10 +415,7 @@
* in the system directory, so it is not safe while actively writing history.
*/
public BatteryStatsHistory copy() {
- // Make a copy of battery history to avoid concurrent modification.
- Parcel historyBuffer = Parcel.obtain();
- historyBuffer.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
- return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null, mTracer);
+ return new BatteryStatsHistory(this);
}
/**
@@ -446,6 +474,25 @@
Slog.e(TAG, "Could not create history file: " + mActiveFile.getBaseFile());
}
+ synchronized (this) {
+ cleanupLocked();
+ }
+ }
+
+ @GuardedBy("this")
+ private void setCleanupEnabledLocked(boolean enabled) {
+ mCleanupEnabled = enabled;
+ if (mCleanupEnabled) {
+ cleanupLocked();
+ }
+ }
+
+ @GuardedBy("this")
+ private void cleanupLocked() {
+ if (!mCleanupEnabled || mHistoryDir == null) {
+ return;
+ }
+
// if free disk space is less than 100MB, delete oldest history file.
if (!hasFreeDiskSpace()) {
int oldest = mFileNumbers.remove(0);
@@ -463,6 +510,16 @@
}
/**
+ * Returns true if it is safe to reset history. It will return false if the history is
+ * currently being read.
+ */
+ public boolean isResetEnabled() {
+ synchronized (this) {
+ return mCleanupEnabled;
+ }
+ }
+
+ /**
* Clear history buffer and delete all existing history files. Active history file start from
* number 0 again.
*/
@@ -489,6 +546,11 @@
mCurrentParcelEnd = 0;
mParcelIndex = 0;
mMutable = false;
+ if (mWritableHistory != null) {
+ synchronized (mWritableHistory) {
+ mWritableHistory.setCleanupEnabledLocked(false);
+ }
+ }
return new BatteryStatsHistoryIterator(this);
}
@@ -498,7 +560,13 @@
void iteratorFinished() {
// setDataPosition so mHistoryBuffer Parcel can be written.
mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
- mMutable = true;
+ if (mWritableHistory != null) {
+ synchronized (mWritableHistory) {
+ mWritableHistory.setCleanupEnabledLocked(true);
+ }
+ } else {
+ mMutable = true;
+ }
}
/**
@@ -715,15 +783,7 @@
* the {@link #writeToBatteryUsageStatsParcel} method.
*/
public static BatteryStatsHistory createFromBatteryUsageStatsParcel(Parcel in) {
- final byte[] historyBlob = in.readBlob();
-
- Parcel historyBuffer = Parcel.obtain();
- historyBuffer.unmarshall(historyBlob, 0, historyBlob.length);
-
- BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer, null,
- Clock.SYSTEM_CLOCK);
- history.readFromParcel(in, true /* useBlobs */);
- return history;
+ return new BatteryStatsHistory(in);
}
/**
@@ -1385,8 +1445,17 @@
if (dataSize == 0) {
// The history is currently empty; we need it to start with a time stamp.
- cur.currentTime = mClock.currentTimeMillis();
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_RESET);
+ HistoryItem copy = new HistoryItem();
+ copy.setTo(cur);
+ copy.currentTime = mClock.currentTimeMillis();
+ copy.wakelockTag = null;
+ copy.wakeReasonTag = null;
+ copy.eventCode = HistoryItem.EVENT_NONE;
+ copy.eventTag = null;
+ copy.tagsFirstOccurrence = false;
+ copy.energyConsumerDetails = null;
+ copy.cpuUsageDetails = null;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_RESET);
}
writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE);
}
@@ -1516,10 +1585,19 @@
deltaTimeToken = (int) deltaTime;
}
int firstToken = deltaTimeToken | (cur.states & BatteryStatsHistory.DELTA_STATE_MASK);
- final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
- ? BatteryStatsHistory.BATTERY_DELTA_LEVEL_FLAG : 0;
- mLastHistoryStepLevel = cur.batteryLevel;
- final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
+ int batteryLevelInt = buildBatteryLevelInt(cur);
+
+ if (cur.batteryLevel < mLastHistoryStepLevel || mLastHistoryStepLevel == 0) {
+ cur.stepDetails = mStepDetailsCalculator.getHistoryStepDetails();
+ if (cur.stepDetails != null) {
+ batteryLevelInt |= BatteryStatsHistory.BATTERY_LEVEL_DETAILS_FLAG;
+ mLastHistoryStepLevel = cur.batteryLevel;
+ }
+ } else {
+ cur.stepDetails = null;
+ mLastHistoryStepLevel = cur.batteryLevel;
+ }
+
final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
if (batteryLevelIntChanged) {
firstToken |= BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG;
@@ -1652,8 +1730,7 @@
}
}
- cur.stepDetails = mStepDetailsCalculator.getHistoryStepDetails();
- if (includeStepDetails != 0) {
+ if (cur.stepDetails != null) {
cur.stepDetails.writeToParcel(dest);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 67eee4f..ccc3454 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -215,7 +215,7 @@
cur.eventCode = BatteryStats.HistoryItem.EVENT_NONE;
}
- if ((batteryLevelInt & BatteryStatsHistory.BATTERY_DELTA_LEVEL_FLAG) != 0) {
+ if ((batteryLevelInt & BatteryStatsHistory.BATTERY_LEVEL_DETAILS_FLAG) != 0) {
cur.stepDetails = mReadHistoryStepDetails;
cur.stepDetails.readFromParcel(src);
} else {
diff --git a/core/java/com/android/internal/os/IBinaryTransparencyService.aidl b/core/java/com/android/internal/os/IBinaryTransparencyService.aidl
index a1ad5d5..ca7b83e 100644
--- a/core/java/com/android/internal/os/IBinaryTransparencyService.aidl
+++ b/core/java/com/android/internal/os/IBinaryTransparencyService.aidl
@@ -28,5 +28,27 @@
List getApexInfo();
- List getMeasurementsForAllPackages();
+ void recordMeasurementsForAllPackages();
+
+ parcelable ApexInfo {
+ String packageName;
+ long longVersion;
+ byte[] digest;
+ int digestAlgorithm;
+ String[] signerDigests;
+ }
+
+ parcelable AppInfo {
+ String packageName;
+ long longVersion;
+ String splitName;
+ byte[] digest;
+ int digestAlgorithm;
+ String[] signerDigests;
+ int mbaStatus;
+ String initiator;
+ String[] initiatorSignerDigests;
+ String installer;
+ String originator;
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index deafd19..cee8c1a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -178,7 +178,14 @@
* GWP-ASan is activated unconditionally (but still, only a small subset of
* allocations is protected).
*/
- public static final int GWP_ASAN_LEVEL_ALWAYS = 1 << 22;
+ public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
+
+ /**
+ * GWP-ASan's `gwpAsanMode` manifest flag was unspecified. Currently, this
+ * means GWP_ASAN_LEVEL_LOTTERY for system apps, and GWP_ASAN_LEVEL_NONE for
+ * non-system apps.
+ */
+ public static final int GWP_ASAN_LEVEL_DEFAULT = 3 << 21;
/** Enable automatic zero-initialization of native heap memory allocations. */
public static final int NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23;
@@ -1328,6 +1335,15 @@
level = MEMORY_TAG_LEVEL_NONE;
}
+ // If we requested "sync" mode for the whole platform, upgrade mode for apps that enable
+ // MTE.
+ // This makes debugging a lot easier.
+ if (level == MEMORY_TAG_LEVEL_ASYNC
+ && (Build.IS_USERDEBUG || Build.IS_ENG)
+ && "sync".equals(SystemProperties.get("persist.arm64.memtag.default"))) {
+ level = MEMORY_TAG_LEVEL_SYNC;
+ }
+
return level;
}
@@ -1347,15 +1363,13 @@
? GWP_ASAN_LEVEL_ALWAYS
: GWP_ASAN_LEVEL_NEVER;
}
- // If the app does not specify gwpAsanMode, the default behavior is lottery among the
- // system apps, and disabled for user apps, unless overwritten by the compat feature.
if (isCompatChangeEnabled(GWP_ASAN, info, platformCompat, 0)) {
return GWP_ASAN_LEVEL_ALWAYS;
}
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return GWP_ASAN_LEVEL_LOTTERY;
}
- return GWP_ASAN_LEVEL_NEVER;
+ return GWP_ASAN_LEVEL_DEFAULT;
}
private static boolean enableNativeHeapZeroInit(
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 91d2ecb..d8dffcd 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -219,7 +219,7 @@
"android_security_Scrypt.cpp",
"com_android_internal_content_om_OverlayConfig.cpp",
"com_android_internal_content_om_OverlayManagerImpl.cpp",
- "com_android_internal_expresslog_Counter.cpp",
+ "com_android_internal_expresslog_Utils.cpp",
"com_android_internal_net_NetworkUtilsInternal.cpp",
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
@@ -266,7 +266,6 @@
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
"libandroid_net",
- "libandroidicu",
"libbattery",
"libnetdutils",
"libmemtrack",
@@ -287,6 +286,7 @@
"libPlatformProperties",
"libsensor",
"libinput",
+ "libicu",
"libcamera_client",
"libcamera_metadata",
"libprocinfo",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 578cf24..b550f28 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -200,7 +200,7 @@
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
extern int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env);
-extern int register_com_android_internal_expresslog_Counter(JNIEnv* env);
+extern int register_com_android_internal_expresslog_Utils(JNIEnv* env);
extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
@@ -1586,7 +1586,7 @@
REG_JNI(register_android_os_incremental_IncrementalManager),
REG_JNI(register_com_android_internal_content_om_OverlayConfig),
REG_JNI(register_com_android_internal_content_om_OverlayManagerImpl),
- REG_JNI(register_com_android_internal_expresslog_Counter),
+ REG_JNI(register_com_android_internal_expresslog_Utils),
REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp
index 9941ca4..34ef7b3 100644
--- a/core/jni/android_hardware_OverlayProperties.cpp
+++ b/core/jni/android_hardware_OverlayProperties.cpp
@@ -60,8 +60,14 @@
if (std::find(i.pixelFormats.begin(), i.pixelFormats.end(),
static_cast<int32_t>(HAL_PIXEL_FORMAT_RGBA_FP16)) !=
i.pixelFormats.end() &&
- std::find(i.dataspaces.begin(), i.dataspaces.end(),
- static_cast<int32_t>(HAL_DATASPACE_BT2020_PQ)) != i.dataspaces.end()) {
+ std::find(i.standards.begin(), i.standards.end(),
+ static_cast<int32_t>(HAL_DATASPACE_STANDARD_BT2020)) !=
+ i.standards.end() &&
+ std::find(i.transfers.begin(), i.transfers.end(),
+ static_cast<int32_t>(HAL_DATASPACE_TRANSFER_ST2084)) !=
+ i.transfers.end() &&
+ std::find(i.ranges.begin(), i.ranges.end(),
+ static_cast<int32_t>(HAL_DATASPACE_RANGE_FULL)) != i.ranges.end()) {
return true;
}
}
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index ae45a0e..1557f9e 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -19,18 +19,18 @@
#include "android_media_AudioTrack.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include "core_jni_helpers.h"
-
-#include <utils/Log.h>
+#include <android-base/macros.h>
+#include <android_os_Parcel.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
#include <media/AudioParameter.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
-#include <android-base/macros.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
+#include <cinttypes>
#include "android_media_AudioAttributes.h"
#include "android_media_AudioErrors.h"
@@ -41,8 +41,7 @@
#include "android_media_MediaMetricsJNI.h"
#include "android_media_PlaybackParams.h"
#include "android_media_VolumeShaper.h"
-
-#include <cinttypes>
+#include "core_jni_helpers.h"
// ----------------------------------------------------------------------------
@@ -245,9 +244,10 @@
jobject jaa, jintArray jSampleRate,
jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode,
- jintArray jSession, jlong nativeAudioTrack,
- jboolean offload, jint encapsulationMode,
- jobject tunerConfiguration, jstring opPackageName) {
+ jintArray jSession, jobject jAttributionSource,
+ jlong nativeAudioTrack, jboolean offload,
+ jint encapsulationMode, jobject tunerConfiguration,
+ jstring opPackageName) {
ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
" nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p",
jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
@@ -323,10 +323,9 @@
// create the native AudioTrack object
ScopedUtfChars opPackageNameStr(env, opPackageName);
- // TODO b/182469354: make consistent with AudioRecord
- AttributionSourceState attributionSource;
- attributionSource.packageName = std::string(opPackageNameStr.c_str());
- attributionSource.token = sp<BBinder>::make();
+
+ android::content::AttributionSourceState attributionSource;
+ attributionSource.readFromParcel(parcelForJavaObject(env, jAttributionSource));
lpTrack = sp<AudioTrack>::make(attributionSource);
// read the AudioAttributes values
@@ -382,7 +381,7 @@
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
: AudioTrack::TRANSFER_SYNC,
(offload || encapsulationMode) ? &offloadInfo : NULL,
- AttributionSourceState(), // default uid, pid values
+ attributionSource, // Passed from Java
paa.get());
break;
@@ -401,14 +400,14 @@
format, // word length, PCM
nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE,
lpJniStorage,
- 0, // notificationFrames == 0 since not using EVENT_MORE_DATA
- // to feed the AudioTrack
- iMem, // shared mem
- true, // thread can call Java
- sessionId, // audio session ID
+ 0, // notificationFrames == 0 since not using EVENT_MORE_DATA
+ // to feed the AudioTrack
+ iMem, // shared mem
+ true, // thread can call Java
+ sessionId, // audio session ID
AudioTrack::TRANSFER_SHARED,
- nullptr , // default offloadInfo
- AttributionSourceState(), // default uid, pid values
+ nullptr, // default offloadInfo
+ attributionSource, // Passed from Java
paa.get());
break;
}
@@ -1456,7 +1455,8 @@
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
{"native_flush", "()V", (void *)android_media_AudioTrack_flush},
{"native_setup",
- "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;Ljava/lang/String;)I",
+ "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[ILandroid/os/Parcel;"
+ "JZILjava/lang/Object;Ljava/lang/String;)I",
(void *)android_media_AudioTrack_setup},
{"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
{"native_release", "()V", (void *)android_media_AudioTrack_release},
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index 0223b96..d8a2497 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -41,7 +41,7 @@
typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_closeSession)(APerformanceHintSession* session);
typedef void (*APH_sendHint)(APerformanceHintSession*, int32_t);
-typedef void (*APH_setThreads)(APerformanceHintSession*, const int32_t*, size_t);
+typedef void (*APH_setThreads)(APerformanceHintSession*, const pid_t*, size_t);
typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const);
bool gAPerformanceHintBindingInitialized = false;
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 2ef9632..d5568df 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -157,7 +157,7 @@
static jint android_view_KeyEvent_nativeKeyCodeFromString(JNIEnv* env, jobject clazz,
jstring label) {
ScopedUtfChars keyLabel(env, label);
- return KeyEvent::getKeyCodeFromLabel(keyLabel.c_str());
+ return KeyEvent::getKeyCodeFromLabel(keyLabel.c_str()).value_or(AKEYCODE_UNKNOWN);
}
static jint android_view_KeyEvent_nativeNextId() {
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 88444cb..49f47c5 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -515,7 +515,7 @@
static jint android_view_MotionEvent_nativeAxisFromString(JNIEnv* env, jclass clazz,
jstring label) {
ScopedUtfChars axisLabel(env, label);
- return static_cast<jint>(MotionEvent::getAxisFromLabel(axisLabel.c_str()));
+ return static_cast<jint>(MotionEvent::getAxisFromLabel(axisLabel.c_str()).value_or(-1));
}
// ---------------- @FastNative ----------------------------------
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 34589b7..225ed2c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -579,6 +579,14 @@
transaction->setDataspace(ctrl, dataspace);
}
+static void nativeSetExtendedRangeBrightness(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, float currentBufferRatio,
+ float desiredRatio) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setExtendedRangeBrightness(ctrl, currentBufferRatio, desiredRatio);
+}
+
static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobjectArray regions, jint regionsLength) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2086,6 +2094,8 @@
{"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform},
{"nativeSetDataSpace", "(JJI)V",
(void*)nativeSetDataSpace },
+ {"nativeSetExtendedRangeBrightness", "(JJFF)V",
+ (void*)nativeSetExtendedRangeBrightness },
{"nativeAddWindowInfosReportedListener", "(JLjava/lang/Runnable;)V",
(void*)nativeAddWindowInfosReportedListener },
{"nativeGetDisplayBrightnessSupport", "(Landroid/os/IBinder;)Z",
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 6762e45..2c81987 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "NativeLibraryHelper"
//#define LOG_NDEBUG 0
+#include <androidfw/ApkParsing.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
#include <errno.h>
@@ -37,15 +38,6 @@
#include "core_jni_helpers.h"
-#define APK_LIB "lib/"
-#define APK_LIB_LEN (sizeof(APK_LIB) - 1)
-
-#define LIB_PREFIX "/lib"
-#define LIB_PREFIX_LEN (sizeof(LIB_PREFIX) - 1)
-
-#define LIB_SUFFIX ".so"
-#define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1)
-
#define RS_BITCODE_SUFFIX ".bc"
#define TMP_FILE_PATTERN "/tmp.XXXXXX"
@@ -66,39 +58,6 @@
typedef install_status_t (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*);
-// Equivalent to android.os.FileUtils.isFilenameSafe
-static bool
-isFilenameSafe(const char* filename)
-{
- off_t offset = 0;
- for (;;) {
- switch (*(filename + offset)) {
- case 0:
- // Null.
- // If we've reached the end, all the other characters are good.
- return true;
-
- case 'A' ... 'Z':
- case 'a' ... 'z':
- case '0' ... '9':
- case '+':
- case ',':
- case '-':
- case '.':
- case '/':
- case '=':
- case '_':
- offset++;
- break;
-
- default:
- // We found something that is not good.
- return false;
- }
- }
- // Should not reach here.
-}
-
static bool
isFileDifferent(const char* filePath, uint32_t fileSize, time_t modifiedTime,
uint32_t zipCrc, struct stat64* st)
@@ -330,7 +289,7 @@
static NativeLibrariesIterator* create(ZipFileRO* zipFile, bool debuggable) {
void* cookie = nullptr;
// Do not specify a suffix to find both .so files and gdbserver.
- if (!zipFile->startIteration(&cookie, APK_LIB, nullptr /* suffix */)) {
+ if (!zipFile->startIteration(&cookie, APK_LIB.data(), nullptr /* suffix */)) {
return nullptr;
}
@@ -345,36 +304,11 @@
continue;
}
- // Make sure the filename is at least to the minimum library name size.
- const size_t fileNameLen = strlen(fileName);
- static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
- if (fileNameLen < minLength) {
- continue;
+ const char* lastSlash = util::ValidLibraryPathLastSlash(fileName, false, mDebuggable);
+ if (lastSlash) {
+ mLastSlash = lastSlash;
+ break;
}
-
- const char* lastSlash = strrchr(fileName, '/');
- ALOG_ASSERT(lastSlash != nullptr, "last slash was null somehow for %s\n", fileName);
-
- // Skip directories.
- if (*(lastSlash + 1) == 0) {
- continue;
- }
-
- // Make sure the filename is safe.
- if (!isFilenameSafe(lastSlash + 1)) {
- continue;
- }
-
- if (!mDebuggable) {
- // Make sure the filename starts with lib and ends with ".so".
- if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
- || strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)) {
- continue;
- }
- }
-
- mLastSlash = lastSlash;
- break;
}
return next;
@@ -543,7 +477,7 @@
}
const char* lastSlash = strrchr(fileName, '/');
const char* baseName = (lastSlash == nullptr) ? fileName : fileName + 1;
- if (isFilenameSafe(baseName)) {
+ if (util::isFilenameSafe(baseName)) {
zipFile->endIteration(cookie);
return BITCODE_PRESENT;
}
diff --git a/core/jni/com_android_internal_expresslog_Counter.cpp b/core/jni/com_android_internal_expresslog_Utils.cpp
similarity index 83%
rename from core/jni/com_android_internal_expresslog_Counter.cpp
rename to core/jni/com_android_internal_expresslog_Utils.cpp
index d4a8c23..d33a7bd 100644
--- a/core/jni/com_android_internal_expresslog_Counter.cpp
+++ b/core/jni/com_android_internal_expresslog_Utils.cpp
@@ -26,7 +26,7 @@
static jclass g_stringClass = nullptr;
/**
- * Class: com_android_internal_expresslog_Counter
+ * Class: com_android_internal_expresslog_Utils
* Method: hashString
* Signature: (Ljava/lang/String;)J
*/
@@ -43,15 +43,15 @@
{"hashString", "(Ljava/lang/String;)J", (void*)hashString},
};
-static const char* const kCounterPathName = "com/android/internal/expresslog/Counter";
+static const char* const kUtilsPathName = "com/android/internal/expresslog/Utils";
namespace android {
-int register_com_android_internal_expresslog_Counter(JNIEnv* env) {
+int register_com_android_internal_expresslog_Utils(JNIEnv* env) {
jclass stringClass = FindClassOrDie(env, "java/lang/String");
g_stringClass = MakeGlobalRefOrDie(env, stringClass);
- return RegisterMethodsOrDie(env, kCounterPathName, g_methods, NELEM(g_methods));
+ return RegisterMethodsOrDie(env, kUtilsPathName, g_methods, NELEM(g_methods));
}
} // namespace android
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 664e964..6a92581 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -353,6 +353,7 @@
GWP_ASAN_LEVEL_NEVER = 0 << 21,
GWP_ASAN_LEVEL_LOTTERY = 1 << 21,
GWP_ASAN_LEVEL_ALWAYS = 2 << 21,
+ GWP_ASAN_LEVEL_DEFAULT = 3 << 21,
NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23,
PROFILEABLE = 1 << 24,
};
@@ -1932,6 +1933,13 @@
gwp_asan_options.program_name = nice_name_ptr ?: process_name;
switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
default:
+ case RuntimeFlags::GWP_ASAN_LEVEL_DEFAULT:
+ // TODO(b/247012630): Switch this to Action::TURN_ON_FOR_APP_SAMPLED_NON_CRASHING once
+ // performance and syshealth testing is completed, making the default for non-system
+ // apps that don't specify a `gwpAsanMode` in their manifest to be sampled-recoverable.
+ gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+ break;
case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN;
android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
diff --git a/core/proto/android/server/syncstorageengine.proto b/core/proto/android/server/syncstorageengine.proto
index d313747..2f35a07 100644
--- a/core/proto/android/server/syncstorageengine.proto
+++ b/core/proto/android/server/syncstorageengine.proto
@@ -83,4 +83,6 @@
}
repeated StatusInfo status = 1;
+
+ optional bool is_job_namespace_migrated = 2;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 82e1777..b112e7a 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -117,7 +117,7 @@
repeated KeyguardOccludedProto keyguard_occluded_states = 2 [deprecated=true];
optional bool aod_showing = 3;
repeated KeyguardPerDisplayProto keyguard_per_display = 4;
- optional bool keyguard_going_away = 5;
+ optional bool keyguard_going_away = 5 [deprecated=true];
}
message KeyguardOccludedProto {
@@ -134,7 +134,7 @@
optional bool keyguard_showing = 2;
optional bool aod_showing = 3;
optional bool keyguard_occluded = 4;
- optional bool keyguard_going_away = 5;
+ optional bool keyguard_going_away = 5 [deprecated=true];
}
/* represents PhoneWindowManager */
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 8e4006a..e029af4 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -110,11 +110,20 @@
// All of this type/caption enabled for current profiles.
repeated android.content.ComponentNameProto enabled = 3;
-
repeated ManagedServiceInfoProto live_services = 4;
+ // Was: repeated ComponentNameProto, when snoozed services were not per-user-id.
+ reserved 5;
+
+ message SnoozedServices {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 user_id = 1;
+ repeated android.content.ComponentNameProto snoozed = 2;
+ }
+
// Snoozed for current profiles.
- repeated android.content.ComponentNameProto snoozed = 5;
+ repeated SnoozedServices snoozed = 6;
}
message RankingHelperProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bc40cef..7d69d56 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1733,6 +1733,34 @@
android:protectionLevel="dangerous"
android:permissionFlags="hardRestricted" />
+ <!-- Allows an application to access wrist temperature data from the watch sensors.
+ <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_bodySensorsWristTemperature"
+ android:description="@string/permdesc_bodySensorsWristTemperature"
+ android:backgroundPermission="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to access wrist temperature data from the watch sensors.
+ If you're requesting this permission, you must also request
+ {@link #BODY_SENSORS_WRIST_TEMPERATURE}. Requesting this permission by itself doesn't
+ give you heart rate body sensors access.
+ <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_bodySensors_wristTemperature_background"
+ android:description="@string/permdesc_bodySensors_wristTemperature_background"
+ android:protectionLevel="dangerous"
+ android:permissionFlags="hardRestricted" />
+
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
@deprecated Applications should request {@link
@@ -4125,7 +4153,7 @@
<p>Should only be requested by the System, should be required by
TileService declarations.-->
<permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|recents" />
<!-- Allows SystemUI to request third party controls.
<p>Should only be requested by the System and required by
@@ -5244,6 +5272,12 @@
<permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE"
android:protectionLevel="signature" />
+ <!-- Allows an application to modify the HDR conversion mode.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -7035,6 +7069,17 @@
<permission android:name="android.permission.GET_APP_METADATA"
android:protectionLevel="signature|installer" />
+ <!-- @hide @SystemApi Allows an application to stage HealthConnect's remote data so that
+ HealthConnect can later integrate it. -->
+ <permission android:name="android.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/config_healthConnectStagingDataKnownSigners"/>
+
+ <!-- @hide @TestApi Allows an application to clear HealthConnect's staged remote data for
+ testing only. For security reasons, this is a platform-only permission. -->
+ <permission android:name="android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows the holder to call health connect migration APIs.
@hide -->
<permission android:name="android.permission.MIGRATE_HEALTH_CONNECT_DATA"
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 50e6f33..a5ff470 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -17,6 +17,7 @@
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 8c1b6ed..2652da6 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -326,11 +326,11 @@
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"wys kennisgewings"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Venster-inhoud ophaal"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Die inhoud ondersoek van \'n venster waarmee jy interaksie het."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Verken deur raak aanskakel"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Verken-met-raak aanskakel"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Items waarop getik word, sal hardop gesê word en die skerm kan met behulp van gebare verken word."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Teks wat jy tik waarneem"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Neem teks wat jy tik waar"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Dit sluit persoonlike data soos kredietkaartnommers en wagwoorde in."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Vertoonskermvergroting beheer"</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Beheer vertoonskerm-vergroting"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Die vertoonskerm se zoemvlak en posisionering beheer."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Voer gebare uit"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Kan tik, swiep, knyp en ander gebare uitvoer."</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Skakel werkprogramme aan?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Kry toegang tot jou werkprogramme en -kennisgewings"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Skakel aan"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Noodgeval"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Kry toegang tot jou werkapps en -oproepe"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Program is nie beskikbaar nie"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is nie op die oomblik beskikbaar nie."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> is nie beskikbaar nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 708b9fd..d47bcab 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"የሥራ መተግበሪያዎች ይብሩ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"የእርስዎን የሥራ መተግበሪያዎች እና ማሳወቂያዎች መዳረሻ ያግኙ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"አብራ"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ድንገተኛ አደጋ"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"የእርስዎን የሥራ መተግበሪያዎች እና ጥሪዎች መዳረሻ ያግኙ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"መተግበሪያ አይገኝም"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> አሁን አይገኝም።"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> አይገኝም"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e524fc7..b443e45 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1947,10 +1947,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"هل تريد تفعيل تطبيقات العمل؟"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"الوصول إلى تطبيقات العمل وإشعاراتها"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"تفعيل"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"الطوارئ"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"الوصول إلى المكالمات وتطبيقات العمل"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"التطبيق غير متاح"</string>
<string name="app_blocked_message" msgid="542972921087873023">"تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> غير متاح الآن."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"تطبيق <xliff:g id="ACTIVITY">%1$s</xliff:g> غير متاح"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 5409045..e39a783 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -822,7 +822,7 @@
<string name="policylab_expirePassword" msgid="6015404400532459169">"স্ক্ৰীন লক পাছৱৰ্ডৰ ম্যাদ ওকলাৰ দিন ছেট কৰক"</string>
<string name="policydesc_expirePassword" msgid="9136524319325960675">"স্ক্ৰীন লকৰ পাছৱৰ্ড, পিন বা আর্হি কিমান ঘনাই সলনি কৰিব লাগিব তাক সলনি কৰক।"</string>
<string name="policylab_encryptedStorage" msgid="9012936958126670110">"ষ্ট’ৰেজৰ এনক্ৰিপশ্বন ছেট কৰক"</string>
- <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"সঞ্চয় কৰি ৰখা ডেটাক এনক্ৰিপ্ট কৰাৰ প্ৰয়োজন।"</string>
+ <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"ষ্ট’ৰ কৰি ৰখা এপৰ ডেটাক এনক্ৰিপ্ট কৰাৰ প্ৰয়োজন।"</string>
<string name="policylab_disableCamera" msgid="5749486347810162018">"কেমেৰাবোৰ অক্ষম কৰক"</string>
<string name="policydesc_disableCamera" msgid="3204405908799676104">"আটাইবোৰ ডিভাইচৰ কেমেৰা ব্যৱহাৰ কৰাত বাধা দিয়ক।"</string>
<string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"স্ক্ৰীন লকৰ কিছুমান সুবিধা অক্ষম কৰক"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"কৰ্মস্থানৰ এপ্ অন কৰিবনে?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"আপোনাৰ কৰ্মস্থানৰ এপ্ আৰু জাননীৰ এক্সেছ পাওক"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"অন কৰক"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"জৰুৰীকালীন"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"আপোনাৰ কৰ্মস্থানৰ এপ্ আৰু জাননীৰ এক্সেছ পাওক"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"এপ্টো উপলব্ধ নহয়"</string>
<string name="app_blocked_message" msgid="542972921087873023">"এই মুহূৰ্তত <xliff:g id="APP_NAME">%1$s</xliff:g> উপলব্ধ নহয়।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> উপলব্ধ নহয়"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index efa6c67..73bf38b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"İş tətbiqləri aktiv edilsin?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"İş tətbiqlərinizə və bildirişlərinizə giriş əldə edin"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivləşdirin"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Fövqəladə hal"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"İş tətbiqlərinizə və zənglərinizə giriş əldə edin"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Tətbiq əlçatan deyil"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hazırda əlçatan deyil."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> əlçatan deyil"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0677369..237aea2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Uključujete poslovne aplikacije?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Pristupajte poslovnim aplikacijama i obaveštenjima"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hitan slučaj"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Pristupajte poslovnim aplikacijama i pozivima"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – nije dostupno"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 07cdb89..aae5f56 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Уключыць працоўныя праграмы?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Атрымаць доступ да працоўных праграм і апавяшчэнняў"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Уключыць"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Экстранны выпадак"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Запытайце доступ да працоўных праграм і выклікаў"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Праграма недаступная"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" цяпер недаступная."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недаступна: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 4139e95..f4d6749 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -324,17 +324,17 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"достъп до сензорните данни за жизнените ви показатели"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Известия"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"показване на известията"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Извличане на съдържанието от прозореца"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Инспектиране на съдържанието на прозорец, с който взаимодействате."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Включване на изследването чрез докосване"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Извлича съдържанието от прозореца"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Инспектира съдържанието на прозорец, с който взаимодействате."</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Включи изследването чрез докосване"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Докосваните елементи ще бъдат изговаряни на глас и екранът може да бъде изследван посредством жестове."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Наблюдение на въвеждания от вас текст"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Наблюдава въвеждания от вас текст"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Включва лични данни, като например номера на кредитни карти и пароли."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Управление на увеличението на дисплея"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Управление на нивото на мащаба и позиционирането на дисплея."</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Управлява увеличението на дисплея"</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Управлява нивото на мащаба и позиционирането на дисплея."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Извършване на жестове"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Можете да докосвате, да прекарвате пръст, да събирате пръсти и да извършвате други жестове."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Жестове за отпечатък"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Улавя жестове за отпечатък"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Може да улавя жестовете, извършени върху сензора за отпечатъци на устройството."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Създаване на екранна снимка"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Може да създава екранни снимки."</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Включване на служ. приложения?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Получете достъп до служебните си приложения и известия"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Включване"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Спешен случай"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Получете достъп до служебните си приложения и обаждания"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Приложението не е достъпно"</string>
<string name="app_blocked_message" msgid="542972921087873023">"В момента няма достъп до <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> не е налице"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 09e592c..e669b6d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -324,13 +324,13 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"আপনার অত্যাবশ্যক লক্ষণগুলির সম্পর্কে সেন্সর ডেটা অ্যাক্সেস করে"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"বিজ্ঞপ্তি"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"বিজ্ঞপ্তি দেখুন"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"উইন্ডোর কন্টেন্ট পুনরুদ্ধার করে"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ব্যবহার করছেন এমন একটি উইন্ডোর কন্টেন্ট নিরীক্ষণ করে৷"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"উইন্ডোর কন্টেন্ট ফিরিয়ে আনুন"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ব্যবহার করছেন এমন একটি উইন্ডোর কন্টেন্ট পরীক্ষা করে৷"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"স্পর্শের মাধ্যমে অন্বেষণ করা চালু করুন"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"যে আইটেমগুলিতে আলতো চেপেছেন সেগুলি সশব্দে বলবে এবং ইঙ্গিতগুলি ব্যবহার করে স্ক্রিন অন্বেষণ করা যাবে৷"</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"যে আইটেমগুলিতে ট্যাপ করেছেন সেগুলি জোরে বলবে এবং ইঙ্গিতগুলি ব্যবহার করে স্ক্রিন অন্বেষণ করা যাবে৷"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"আপনার লেখা পাঠ্যকে নিরীক্ষণ করে"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"ক্রেডিট কার্ডের নম্বর ও পাসওয়ার্ডগুলির মতো ব্যক্তিগত তথ্য অন্তর্ভুক্ত করে৷"</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"প্রদর্শনের বৃহত্তরীকরণ ব্যবস্থা নিয়ন্ত্রণ করুন"</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"ডিসপ্লে বড়কার ব্যবস্থা নিয়ন্ত্রণ করুন"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"প্রদর্শনের জুমের স্তর এবং লোকেশন নির্ধারন নিয়ন্ত্রণ করুন৷"</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"অঙ্গভঙ্গির কাজগুলি সম্পাদন"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"আলতো চাপ দেওয়া, সোয়াইপ, পিঞ্চ করা এবং অন্যান্য ইঙ্গিতের কাজগুলি সম্পাদন করতে পারবেন৷"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"অফিস অ্যাপ চালু করবেন?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"আপনার অফিস অ্যাপ এবং বিজ্ঞপ্তিতে অ্যাক্সেস পান"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"চালু করুন"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"জরুরি"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"আপনার অফিসের অ্যাপ এবং কলে অ্যাক্সেস পান"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"অ্যাপ পাওয়া যাচ্ছে না"</string>
<string name="app_blocked_message" msgid="542972921087873023">"এই মুহূর্তে <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপ পাওয়া যাচ্ছে না।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> উপলভ্য নেই"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 4bc3399..1e88766 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -325,20 +325,20 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"pristupa podacima senzora o vašim vitalnim funkcijama"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Obavještenja"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"prikaz obavještenja"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"preuzima sadržaj prozora"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"preuzimati sadržaj prozora"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Pregleda sadržaj prozora koji trenutno koristite."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"uključi opciju Istraživanje dodirom"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Stavke koje dodirnete bit će izgovorene naglas, a ekran možete istraživati koristeći pokrete."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"prati tekst koji unosite"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"uključiti Istraživanje dodirom"</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Stavke koje dodirnete će se izgovarati naglas i moći ćete istraživati ekran pomoću pokreta."</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"pratiti tekst koji unosite"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Obuhvata lične podatke kao što su brojevi kreditnih kartica i lozinke."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"kontrolira uvećavanje prikaza na ekranu"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Kontrolira stepen uvećanja prikaza na ekranu i podešavanje položaja."</string>
- <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"izvodi pokrete"</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"kontrolirati uvećavanje prikaza na ekranu"</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Kontrolira nivo i položaj zumiranja na ekranu."</string>
+ <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"izvoditi pokrete"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Može dodirivati, prevlačiti, hvatati prstima i praviti druge pokrete."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"prepoznaje pokrete za otisak prsta"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Moguće je zabilježiti pokrete na senzoru za otisak prsta uređaja."</string>
- <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"pravi snimke ekrana"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Moguće je snimiti ekran."</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"prepoznavati pokrete otiska prsta"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Može zabilježiti pokrete na senzoru za otisak prsta uređaja."</string>
+ <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"praviti snimke ekrana"</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Može napraviti snimak ekrana."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"onemogućavanje ili mijenjanje statusne trake"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Dozvoljava aplikaciji onemogućavanje statusne trake ili dodavanje i uklanjanje sistemskih ikona."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"funkcioniranje u vidu statusne trake"</string>
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Pristupite poslovnim aplikacijama i obavještenjima"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hitan slučaj"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Ostvarite pristup poslovnim aplikacijama i pozivima"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Nedostupno: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index b863fb5..5843095 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -326,19 +326,19 @@
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificacions"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostra notificacions"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contingut de la finestra"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contingut d\'una finestra amb què estàs interaccionant."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspeccionar el contingut d\'una finestra amb què estàs interaccionant."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar Exploració tàctil"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Els elements que toquis es diran en veu alta, i podràs explorar la pantalla amb gestos."</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Els elements que toquis s\'enunciaran en veu alta, i la pantalla es pot explorar amb gestos."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Observar el text que escrius"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Inclou dades personals com ara números de targetes de crèdit i contrasenyes."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controlar l\'ampliació de la pantalla"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controla el nivell i la posició del zoom de la pantalla."</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlar el nivell i la posició del zoom de la pantalla."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Fer gestos"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Permet tocar, lliscar, pinçar i fer altres gestos."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos d\'empremtes digitals"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Captura gestos realitzats en el sensor d\'empremtes digitals del dispositiu."</string>
- <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fes una captura de pantalla"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pots fer una captura de la pantalla."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Pot capturar els gestos fets en el sensor d\'empremtes digitals del dispositiu."</string>
+ <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fer una captura de pantalla"</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pot fer una captura de la pantalla."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"desactivar o modificar la barra d\'estat"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"aparèixer a la barra d\'estat"</string>
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Activar aplicacions de treball?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Accedeix a les teves aplicacions i notificacions de treball"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activa"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergència"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Obtén accés a les teves trucades i aplicacions de treball"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'aplicació no està disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Ara mateix, <xliff:g id="APP_NAME">%1$s</xliff:g> no està disponible."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no està disponible"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 10b92c8..7e864a4 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Zapnout pracovní aplikace?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Získejte přístup ke svým pracovním aplikacím a oznámením"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Zapnout"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Stav nouze"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Získejte přístup ke svým pracovním aplikacím a hovorům"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikace není k dispozici"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> v tuto chvíli není k dispozici."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> není k dispozici"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index cae119d..29cb818 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Vil du aktivere arbejdsapps?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Få adgang til dine arbejdsapps og notifikationer"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Slå til"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nødopkald"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Få adgang til dine arbejdsapps og opkald"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgængelig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgængelig lige nu."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> er ikke understøttet"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 8d0d56a..29703a5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Geschäftliche Apps aktivieren?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Du erhältst Zugriff auf deine geschäftlichen Apps und Benachrichtigungen"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivieren"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Notruf"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Zugriff auf deine geschäftlichen Apps und Anrufe anfordern"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist derzeit nicht verfügbar."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nicht verfügbar"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index effa751..185308f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Ενεργοπ. εφαρμογών εργασιών;"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Αποκτήστε πρόσβαση στις εφαρμογές εργασιών και τις ειδοποιήσεις"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ενεργοποίηση"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Έκτακτη ανάγκη"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Αποκτήστε πρόσβαση στις εφαρμογές εργασιών και τις κλήσεις"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Η εφαρμογή δεν είναι διαθέσιμη"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν είναι διαθέσιμη αυτήν τη στιγμή."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> δεν διατίθεται"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a83ae5c..94bdb01 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Turn on work apps?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Get access to your work apps and notifications"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Turn on"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Get access to your work apps and calls"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> unavailable"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index c691bb2..b52f527 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Turn on work apps?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Get access to your work apps and notifications"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Turn on"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Get access to your work apps and calls"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> unavailable"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7ec6567..dce46b4 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Turn on work apps?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Get access to your work apps and notifications"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Turn on"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Get access to your work apps and calls"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> unavailable"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 22ccd22..93b4dc8 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"¿Activar apps de trabajo?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Obtén acceso a tus apps de trabajo y notificaciones"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergencia"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Obtén acceso a tus llamadas y apps de trabajo"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"La app no está disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible en este momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no disponible"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index f1f43cd..caefd2c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"¿Activar aplicaciones de trabajo?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Accede a tus aplicaciones y notificaciones de trabajo"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergencia"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Consigue acceso a tus aplicaciones y llamadas de trabajo"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"La aplicación no está disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"En estos momentos, <xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no disponible"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index bb969ad..611c4c0 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Lülitada töörakendused sisse?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Hankige juurdepääs oma töörakendustele ja märguannetele"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Lülita sisse"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hädaolukord"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Hankige juurdepääs oma töörakendustele ja kõnedele"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Rakendus ei ole saadaval"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole praegu saadaval."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ei ole saadaval"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 78a5398..ccf7308 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -324,19 +324,19 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"atzitu bizi-konstanteei buruzko sentsorearen datuak"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Jakinarazpenak"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"jakinarazpenak erakutsi"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Eskuratu leihoko edukia"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Leihoko edukia eskuratu."</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Arakatu irekita daukazun leihoko edukia."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Aktibatu \"Arakatu ukituta\""</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"\"Arakatu ukituta\" aktibatu."</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Sakatutako elementuak ozen irakurriko dira eta pantaila keinu bidez arakatu ahal izango da."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Behatu idazten duzun testua"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Idazten duzun testua behatu."</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Ez da salbuespenik egiten datu pertsonalekin, hala nola kreditu-txartelen zenbakiekin eta pasahitzekin."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Kontrolatu pantailaren zoom-maila"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Kontrolatu pantailaren zoom-maila eta posizioa."</string>
- <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Egin keinuak"</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Pantailaren zoom-maila kontrolatu."</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Pantailaren zoom-maila eta posizioa kontrolatu."</string>
+ <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Keinuak egin."</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Sakatu, lerratu, atximurkatu eta beste hainbat keinu egin ditzake."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Hatz-marken keinuak"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Gailuaren hatz-marken sentsorean egindako keinuak atzeman ditzake."</string>
- <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Atera pantaila-argazki bat"</string>
+ <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Pantaila-argazkiak atera."</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pantaila-argazkiak atera ditzake."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"desgaitu edo aldatu egoera-barra"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Egoera-barra desgaitzea edo sistema-ikonoak gehitzea edo kentzea baimentzen die aplikazioei."</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Laneko aplikazioak aktibatu nahi dituzu?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Atzitu laneko aplikazioak eta jakinarazpenak"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktibatu"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Larrialdia"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Atzitu laneko aplikazioak eta deiak"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikazioa ez dago erabilgarri"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ez dago erabilgarri une honetan."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ez dago erabilgarri"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 9721cfc..8d0e8b1 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"برنامههای کاری روشن شود؟"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"دسترسی به اعلانها و برنامههای کاری"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"روشن کردن"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"اضطراری"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"دسترسی به تماسها و برنامههای کاری"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"برنامه در دسترس نیست"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحالحاضر در دسترس نیست."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> دردسترس نیست"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a4b3568..f327cea 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Käytetäänkö työsovelluksia?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Palauta työsovellukset ja ilmoitukset käyttöön"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ota käyttöön"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hätätilanne"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Palauta työsovellukset ja puhelut käyttöön"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Sovellus ei ole käytettävissä"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole nyt käytettävissä."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ei käytettävissä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 8cc239e..2019938 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Activer applis professionnelles?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Accédez à vos applications professionnelles et à vos notifications"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgence"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Demandez l\'accès à vos applications professionnelles et à vos appels"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'application n\'est pas accessible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas accessible pour le moment."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non accessible"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index db14f84..b58d375 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Activer les applis pro ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Accéder à vos applis et notifications professionnelles"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgence"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Demandez l\'accès à vos applications professionnelles et à vos appels"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Application non disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas disponible pour le moment."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponible"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index e655e69..d998add 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Activar as apps do traballo?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Obtén acceso ás túas aplicacións e notificacións do traballo"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emerxencia"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Obtén acceso ás túas chamadas e aplicacións do traballo"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"A aplicación non está dispoñible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> non está dispoñible neste momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non está dispoñible"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index feb3e9f..3be37c9 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"શું ઑફિસ માટેની ઍપ ચાલુ કરીએ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"તમારી ઑફિસ માટેની ઍપ અને નોટિફિકેશનનો ઍક્સેસ મેળવો"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ચાલુ કરો"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ઇમર્જન્સી"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"તમારી ઑફિસ માટેની ઍપ અને કૉલનો ઍક્સેસ મેળવો"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ઍપ ઉપલબ્ધ નથી"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> હાલમાં ઉપલબ્ધ નથી."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ઉપલબ્ધ નથી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c6d26a2..06a7b5e 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन चालू करने हैं?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"अपने ऑफ़िस के काम से जुड़े ऐप्लिकेशन और सूचनाओं का ऐक्सेस पाएं"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"चालू करें"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"आपातकालीन कॉल"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"अपने वर्क ऐप्लिकेशन और कॉल का ऐक्सेस पाएं"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> इस समय उपलब्ध नहीं है."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध नहीं है"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index b1a6153..135a7c70 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Pristupite svojim poslovnim aplikacijama i obavijestima"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hitni slučaj"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Pristupite svojim poslovnim aplikacijama i pozivima"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutačno nije dostupna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – nije dostupno"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 951336c..c4a0a9b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Bekapcsolja a munkaappokat?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Hozzáférést kaphat munkahelyi alkalmazásaihoz és értesítéseihez"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Bekapcsolás"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Vészhelyzet"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Hozzáférést kaphat munkahelyi alkalmazásaihoz és hívásaihoz"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Az alkalmazás nem hozzáférhető"</string>
<string name="app_blocked_message" msgid="542972921087873023">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg nem hozzáférhető."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"A(z) <xliff:g id="ACTIVITY">%1$s</xliff:g> nem áll rendelkezése"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 29c1121..db6046c 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Միացնե՞լ հավելվածները"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Ձեզ հասանելի կդառնան ձեր աշխատանքային հավելվածներն ու ծանուցումները"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Միացնել"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Արտակարգ իրավիճակ"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Ստացեք ձեր աշխատանքային հավելվածների և զանգերի հասանելիություն"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Հավելվածը հասանելի չէ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն այս պահին հասանելի չէ։"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>՝ անհասանելի է"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index d09926d..c22480a 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Aktifkan aplikasi kerja?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Dapatkan akses ke aplikasi kerja dan notifikasi"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktifkan"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Darurat"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Dapatkan akses ke aplikasi kerja dan panggilan"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikasi tidak tersedia"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia saat ini."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> tidak tersedia"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0057e19..eadd87c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1708,8 +1708,7 @@
<string name="color_correction_feature_name" msgid="7975133554160979214">"Litaleiðrétting"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Einhent stilling"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mjög dökkt"</string>
- <!-- no translation found for hearing_aids_feature_name (1125892105105852542) -->
- <skip />
+ <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Heyrnartæki"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Haltu báðum hljóðstyrkstökkunum inni í þrjár sekúndur til að nota <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1944,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Kveikja á vinnuforritum?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Fá aðgang að vinnuforritum og tilkynningum"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Kveikja"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Neyðartilvik"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Fá aðgang að vinnuforritum og símtölum"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Forrit er ekki tiltækt"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ekki tiltækt núna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ekki í boði"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4ba4b29..e0fa0f4 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Attivare le app di lavoro?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Attiva l\'accesso alle app di lavoro e alle notifiche"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Attiva"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergenza"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Richiedi l\'accesso alle app di lavoro e alle chiamate"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'app non è disponibile"</string>
<string name="app_blocked_message" msgid="542972921087873023">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è al momento disponibile."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non disponibile"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7bb43b0..e84252f 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"להפעיל את האפליקציות לעבודה?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"קבלת גישה להתראות ולאפליקציות בפרופיל העבודה"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"הפעלה"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"שיחת חירום"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"קבלת גישה לשיחות ולאפליקציות בפרופיל העבודה"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"האפליקציה לא זמינה"</string>
<string name="app_blocked_message" msgid="542972921087873023">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא זמינה בשלב זה."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> לא זמינה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 79bd6cb..c384e99 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"仕事用アプリを ON にしますか?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"仕事用のアプリを利用し、通知を受け取れるようになります"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ON にする"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"緊急通報"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"仕事用アプリを利用し、通話を行えるようになります"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"アプリの利用不可"</string>
<string name="app_blocked_message" msgid="542972921087873023">"現在 <xliff:g id="APP_NAME">%1$s</xliff:g> はご利用になれません。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>は利用できません"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index f173973..5e96865 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"გსურთ სამსახურის აპების ჩართვა?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"თქვენი სამსახურის აპებსა და შეტყობინებებზე წვდომის მოპოვება"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ჩართვა"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"საგანგებო სიტუაცია"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"მოიპოვეთ წვდომა თქვენი სამსახურის აპებსა და ზარებზე"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"აპი მიუწვდომელია"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ამჟამად მიუწვდომელია."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> მიუწვდომელია"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index b8aa264..eba758a 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -329,7 +329,7 @@
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Түртілген элементтерді дыбыстау функциясын қосу"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Түртілген элементтер дауыстап айтылады және экранды қимылдар арқылы зерттеуге болады."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Терілген мәтінді тексеру"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Несиелік карта нөмірі және құпия сөздер сияқты жеке деректі қоса."</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Несиелік карта нөмірлері және құпия сөздер сияқты жеке деректерді қамтиды."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Дисплей ұлғайтуды басқару"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Дисплейдің масштабтау деңгейін және орналастыруды басқару."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Қимылдарды орындау"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Жұмыс қолданбаларын қосасыз ба?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Жұмыс қолданбалары мен хабарландыруларына қол жеткізесіз."</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Қосу"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Құтқару қызметіне қоңырау шалу"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Жұмыс қолданбаларын пайдаланып, қоңырауларды қабылдаңыз."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Қолданба қолжетімді емес"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> қазір қолжетімді емес."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> қолжетімсіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 44b6137..85247df 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -325,7 +325,7 @@
<string name="permgrouplab_notifications" msgid="5472972361980668884">"ការជូនដំណឹង"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"បង្ហាញការជូនដំណឹង"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ទាញយកខ្លឹមសារវិនដូ"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ពិនិត្យខ្លឹមសារវិនដូដែលអ្នកកំពុងទាក់ទងជាមួយ។"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ពិនិត្យខ្លឹមសារវិនដូដែលអ្នកកំពុងធ្វើអន្តរកម្មជាមួយ។"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"បើកការរកមើលដោយប៉ះ"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"ធាតុដែលបានប៉ះនឹងត្រូវបានអានឮៗ ហើយអេក្រង់នោះអាចត្រូវបានស្វែងរកដោយប្រើកាយវិការ។"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"មើលអត្ថបទដែលវាយ"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"បើកកម្មវិធីការងារឬ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"ទទួលបានសិទ្ធិចូលប្រើការជូនដំណឹង និងកម្មវិធីការងាររបស់អ្នក"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"បើក"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ពេលមានអាសន្ន"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"សុំសិទ្ធិចូលប្រើប្រាស់កម្មវិធីការងារ និងការហៅរបស់អ្នក"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"មិនអាចប្រើកម្មវិធីនេះបានទេ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"មិនអាចប្រើ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេលនេះបានទេ។"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"មិនអាចប្រើ <xliff:g id="ACTIVITY">%1$s</xliff:g> បានទេ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 1e2a5fa..2ad0200 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"ಕೆಲಸ ಆ್ಯಪ್ಗಳನ್ನು ಆನ್ ಮಾಡಬೇಕೆ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"ನಿಮ್ಮ ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಪಡೆಯಿರಿ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ಆನ್ ಮಾಡಿ"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ತುರ್ತು"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"ನಿಮ್ಮ ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಕರೆಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಪಡೆಯಿರಿ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ಆ್ಯಪ್ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಇದೀಗ ಲಭ್ಯವಿಲ್ಲ."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ಲಭ್ಯವಿಲ್ಲ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index dbcf849..ad429ba 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"직장 앱을 사용 설정하시겠습니까?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"직장 앱 및 알림에 액세스하세요."</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"사용 설정"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"긴급 전화"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"직장 앱 및 전화 통화에 대한 액세스를 요청하세요."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"앱을 사용할 수 없습니다"</string>
<string name="app_blocked_message" msgid="542972921087873023">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> 사용할 수 없음"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index edb0886..d4c4c07 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -30,7 +30,7 @@
<string name="mmiError" msgid="2862759606579822246">"Туташууда көйгөй чыкты же MMI коду жараксыз."</string>
<string name="mmiErrorNotSupported" msgid="5001803469335286099">"Функция колдоого алынбайт."</string>
<string name="mmiFdnError" msgid="3975490266767565852">"Иш-аракет туруктуу терүү номерлери менен гана чектелет."</string>
- <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Роуминг учурунда чалууну башка номерге багыттоонун жөндөөлөрүн телефонуңуздан өзгөртүү мүмкүн эмес."</string>
+ <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Роуминг учурунда чалууну башка номерге багыттоонун параметрлерин телефонуңуздан өзгөртүү мүмкүн эмес."</string>
<string name="serviceEnabled" msgid="7549025003394765639">"Кызмат иштетилди."</string>
<string name="serviceEnabledFor" msgid="1463104778656711613">"Кызмат төмөнкү үчүн иштетилди:"</string>
<string name="serviceDisabled" msgid="641878791205871379">"Кызмат өчүрүлдү."</string>
@@ -72,7 +72,7 @@
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Номурду аныктоонун демейки абалы \"чектелбейт\" деп коюлган. Кийинки чалуу: Чектелген"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Номурду аныктоонун демейки абалы \"чектелбейт\" деп коюлган. Кийинки чалуу: Чектелбейт"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Кызмат камсыздалган эмес."</string>
- <string name="CLIRPermanent" msgid="166443681876381118">"Чалуучунун далдаштырма дайындары жөндөөлөрүн өзгөртө албайсыз."</string>
+ <string name="CLIRPermanent" msgid="166443681876381118">"Чалуучунун далдаштырма дайындары параметрлерин өзгөртө албайсыз."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилдик Интернет <xliff:g id="CARRIERDISPLAY">%s</xliff:g> которулду"</string>
<string name="auto_data_switch_content" msgid="803557715007110959">"Бул функциянын параметрлерин \"Тууралоо\" бөлүмүнөн өзгөртө аласыз"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобилдик Интернет кызматы жок"</string>
@@ -324,7 +324,7 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"организмдин абалына көз салган сенсордун дайындарына мүмкүнчүлүк алуу"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Билдирмелер"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"билдирмелерди көрсөтүү"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Терезедеги мазмунду алып турат"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Терезедеги нерселерди алып туруу"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Учурда ачылып турган терезедеги маалыматты талдайт."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"\"Сыйпалап изилдөө\" мүмкүнчүлүгүн иштетет"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Басылып жаткан элементтерди айтып турат жана түзмөктү жаңсоолор менен башкаруу мүмкүнчүлүгүн иштетет."</string>
@@ -337,7 +337,7 @@
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Манжа изинин жаңсоолору"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Түзмөктөгү манжа изинин сенсорунда жасалган жаңсоолорду жаздырып алат."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Скриншот тартып алуу"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Дисплейдин скриншотун тартып алууга болот."</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Дисплейдин скриншотун тартып алсаңыз болот."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"абал тилкесин өчүрүү же өзгөртүү"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Колдонмого абал тилкесин өчүрүү же тутум сүрөтчөлөрүн кошуу же алып салуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"абал тилкесинин милдетин аткаруу"</string>
@@ -431,7 +431,7 @@
<string name="permlab_getPackageSize" msgid="375391550792886641">"колдонмо сактагычынын мейкиндигин өлчөө"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"Колдонмого өз кодун, дайындарын жана кэш өлчөмдөрүн түшүрүп алуу мүмкүнчүлүгүн берет"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"система тууралоолорун өзгөртүү"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"Колдонмого системанын коопсуздук жөндөөлөрүнүн дайындарын өзгөртүү мүмкүнчүлүгүн берет. Кесепттүү колдонмолор тутумуңуздун конфигурациясын бузуп салышы мүмкүн."</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"Колдонмого системанын коопсуздук параметрлеринин дайындарын өзгөртүү мүмкүнчүлүгүн берет. Кесепттүү колдонмолор тутумуңуздун конфигурациясын бузуп салышы мүмкүн."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"түзмөктү жандырганда иштеп баштоо"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Колдонмого тутум жүктөлүп бүтөөрү менен өзүн-өзү иштетүү мүмкүнчүлүгүн берет. Бул планшеттин ишке киргизилишин кыйла создуктуруп, планшеттин үзгүлтүксүз иштешин жайлатып салышы мүмкүн."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Тутум күйгүзүлөрү менен колдонмого өз алдынча иштеп баштоого уруксат берет. Ага байланыштуу Android TV түзмөгүңүз кечирээк күйгүзүлүп, ошондой эле колдонмо такай иштеп тургандыктан, түзмөк жайыраак иштеп калышы мүмкүн."</string>
@@ -474,7 +474,7 @@
<string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Колдонмо кайда жүргөнүңүздү активдүү режимде гана болжолдуу аныктай алат. Ал үчүн түзмөгүңүздө жайгашкан жерди аныктоо кызматын иштетишиңиз керек."</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"жайгашкан жерди фондо аныктоо"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Колдонмо кайда жүргөнүңүздү активдүү режимде гана эмес, фондук режимде да аныктай алат."</string>
- <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"аудио жөндөөлөрүңүздү өзгөртүңүз"</string>
+ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"аудио параметрлериңизди өзгөртүңүз"</string>
<string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Колдонмого үн деңгээли жана кайсы динамик аркылуу үн чыгарылышы керек сыяктуу түзмөктүн аудио тууралоолорун өзгөртүүгө уруксат берет."</string>
<string name="permlab_recordAudio" msgid="1208457423054219147">"аудио жаздыруу"</string>
<string name="permdesc_recordAudio" msgid="5857246765327514062">"Бул колдонмо иштеп жатканда микрофон менен аудио файлдарды жаздыра алат."</string>
@@ -719,7 +719,7 @@
</string-array>
<string name="face_error_vendor_unknown" msgid="7387005932083302070">"Бир жерден ката кетти. Кайра аракет кылыңыз."</string>
<string name="face_icon_content_description" msgid="465030547475916280">"Жүздүн сүрөтчөсү"</string>
- <string name="permlab_readSyncSettings" msgid="6250532864893156277">"шайкештирүү жөндөөлөрүн окуу"</string>
+ <string name="permlab_readSyncSettings" msgid="6250532864893156277">"шайкештирүү параметрлерин окуу"</string>
<string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Колдонмого эсеп менен синхрондошуу тууралоолорун окуганга уруксат берет. Мисалы, Кишилер колдонмосу эсеп менен синхрондошкондугун аныктай алат."</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"синхрондоштурууну өчүрүү/жандыруу"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Колдонмого эсеп менен синхрондошуу тууралоолорун өзгөртүү уруксатын берет. Мисалы, бул Кишилер колдонмосун эсеп менен синхрондошуусун иштете алат."</string>
@@ -1226,7 +1226,7 @@
<string name="launch_warning_original" msgid="3332206576800169626">"Башында <xliff:g id="APP_NAME">%1$s</xliff:g> жүргүзүлгөн."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Шкала"</string>
<string name="screen_compat_mode_show" msgid="5080361367584709857">"Ар дайым көрүнсүн"</string>
- <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Муну тутум жөндөөлөрүнөн кайра иштетүү > Колдонмолор > Жүктөлүп алынган."</string>
+ <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Муну тутум параметрлеринен кайра иштетүү > Колдонмолор > Жүктөлүп алынган."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу көрүнүштүн тандалган өлчөмүн экранда көрсөтө албайт жана туура эмес иштеши мүмкүн."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Ар дайым көрүнсүн"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS тутуму менен иштеген түзмөктүн шайкеш келбеген версиясы үчүн орнотулган колдонмо жана туура эмес иштеши мүмкүн. Колдонмонун жаңырган версиясы жеткиликтүү болушу мүмкүн."</string>
@@ -1495,7 +1495,7 @@
<string name="vpn_lockdown_connected" msgid="2853127976590658469">"Туташты"</string>
<string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Ар дайым иштеген VPN\'ден ажыратуу"</string>
<string name="vpn_lockdown_error" msgid="4453048646854247947">"Ар дайым күйүк VPN\'ге туташпай калды"</string>
- <string name="vpn_lockdown_config" msgid="8331697329868252169">"Тармакты же VPN жөндөөлөрүн өзгөртүү"</string>
+ <string name="vpn_lockdown_config" msgid="8331697329868252169">"Тармакты же VPN параметрлерин өзгөртүү"</string>
<string name="upload_file" msgid="8651942222301634271">"Файл тандоо"</string>
<string name="no_file_chosen" msgid="4146295695162318057">"Эч файл тандалган жок"</string>
<string name="reset" msgid="3865826612628171429">"Баштапкы абалга келтирүү"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Жумуш колдонмолору күйгүзүлсүнбү?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Жумуш колдонмолоруңузга жана билдирмелериңизге мүмкүнчүлүк алыңыз"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Күйгүзүү"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Шашылыш чалуу"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Жумуш колдонмолоруңузга жана чалууларыңызга мүмкүнчүлүк алыңыз"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Колдонмо учурда жеткиликсиз"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> учурда жеткиликсиз"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> жеткиликсиз"</string>
@@ -2066,7 +2064,7 @@
<string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"Көбүрөөк маалымат алып, өзгөртүү үчүн таптаңыз."</string>
<string name="zen_upgrade_notification_title" msgid="8198167698095298717">"\"Тынчымды алба\" режими өзгөрдү"</string>
<string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Бөгөттөлгөн нерселерди көрүү үчүн таптаңыз."</string>
- <string name="review_notification_settings_title" msgid="5102557424459810820">"Билдирмелердин жөндөөлөрүн карап чыгуу"</string>
+ <string name="review_notification_settings_title" msgid="5102557424459810820">"Билдирмелердин параметрлерин карап чыгуу"</string>
<string name="review_notification_settings_text" msgid="5916244866751849279">"Android 13 версиясынан баштап билдирмелерди жөнөтүү үчүн орноткон колдонмолоруңузга уруксат берүү керек. Учурдагы колдонмолор үчүн бул уруксатты өзгөртүү үчүн таптап коюңуз."</string>
<string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Кийинчерээк эскертүү"</string>
<string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Жабуу"</string>
@@ -2272,7 +2270,7 @@
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Чоңойтуу функциясынын жаңы жөндөөлөрү"</string>
+ <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Чоңойтуу функциясынын жаңы параметрлери"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Эми экрандын бир бөлүгүн чоңойто аласыз"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Жөндөөлөрдөн күйгүзүү"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Жабуу"</string>
@@ -2283,7 +2281,7 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Сенсордун купуялыгы"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Колдонмонун сүрөтчөсү"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Колдонмонун брендинин сүрөтү"</string>
- <string name="view_and_control_notification_title" msgid="4300765399209912240">"Кирүү мүмкүнчүлүгүнүн жөндөөлөрүн текшериңиз"</string>
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Кирүү мүмкүнчүлүгүнүн параметрлерин текшериңиз"</string>
<string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> экраныңызды көрүп, көзөмөлдөй алат. Көрүү үчүн таптап коюңуз."</string>
<string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"Билдирүү (<xliff:g id="MESSAGE">%1$s</xliff:g>) которулду."</string>
<string name="ui_translation_accessibility_translation_finished" msgid="3057830947610088465">"Билдирүү <xliff:g id="FROM_LANGUAGE">%1$s</xliff:g> тилинен <xliff:g id="TO_LANGUAGE">%2$s</xliff:g> тилине которулду."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 1874d27..13a88ca 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"ເປີດໃຊ້ແອັບບ່ອນເຮັດວຽກບໍ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"ຮັບສິດເຂົ້າເຖິງແອັບບ່ອນເຮັດວຽກ ແລະ ການແຈ້ງເຕືອນຂອງທ່ານ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ເປີດ"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ສຸກເສີນ"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"ຮັບສິດເຂົ້າເຖິງແອັບບ່ອນເຮັດວຽກ ແລະ ການໂທຂອງທ່ານ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ແອັບບໍ່ສາມາດໃຊ້ໄດ້"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ສາມາດໃຊ້ໄດ້ໃນຕອນນີ້."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"ບໍ່ສາມາດໃຊ້ <xliff:g id="ACTIVITY">%1$s</xliff:g> ໄດ້"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index b0c481e..6a66751 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Įjungti darbo programas?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Pasiekite darbo programas ir pranešimus"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Įjungti"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Pagalbos tarnyba"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Pasiekite darbo programas ir skambučius"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Programa nepasiekiama."</string>
<string name="app_blocked_message" msgid="542972921087873023">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu nepasiekiama."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"„<xliff:g id="ACTIVITY">%1$s</xliff:g>“ nepasiekiama"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3aca9f8..2bb12db 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Vai ieslēgt darba lietotnes?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Iegūstiet piekļuvi darba lietotnēm un paziņojumiem"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ieslēgt"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Ārkārtas zvans"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Iegūstiet piekļuvi darba lietotnēm un zvaniem"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Lietotne nav pieejama"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik nav pieejama."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nav pieejams"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 28d5969..52019e4 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -324,20 +324,20 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"пристапува до податоците од сензорите за виталните знаци"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Известувања"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"да прикажува известувања"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Преземе содржина на прозорец"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Ја следи содржината на прозорецот со кој се комуницира."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Вклучи „Истражувај со допир“"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Допрените ставки ќе се изговорат на глас и екранот може да се истражува со движења."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Го следи напишаниот текст"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Опфаќа лични податоци како што се броеви на кредитни картички и лозинки."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Го контролира зголемувањето на екранот"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Го контролира нивото на зумирање и позиционирање на екранот."</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"да ги вчитува содржините од прозорците"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"да ги проверува содржините од прозорецот што го користите"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"да вклучи „Истражувај со допир“"</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"допрените ставки ќе се изговараат наглас и екранот ќе може да се истражува со движења"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"да го следи текстот што го пишувате"</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"вклучително и лични податоци како што се броеви на кредитни картички и лозинки"</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"да го контролира зголемувањето на екранот"</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"да го контролира нивото на зумирање и позиционирањето на екранот"</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Користете движења"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Може да допрете, повлечете, штипнете и да користите други движења."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Движења за отпечатоци"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Може да сними движења што се направени на сензорот за отпечатоци на уредот."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Зачувување слика од екранот"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Може да направи слика од екранот."</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Може да зачува слика од екранот."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"оневозможи или измени статусна лента"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Дозволува апликацијата да ја оневозможи статусната лента или да додава или отстранува системски икони."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"да стане статусна лента"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Да се вклучат работни апликации?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Добијте пристап до вашите работни апликации и известувања"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Вклучи"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Итен случај"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Добијте пристап до вашите работни апликации и повици"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апликацијата не е достапна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> не е достапна во моментов."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> е недостапна"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 4076c68..88ce780 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"ഔദ്യോഗിക ആപ്പുകൾ ഓണാക്കണോ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"നിങ്ങളുടെ ഔദ്യോഗിക ആപ്പുകളിലേക്കും അറിയിപ്പുകളിലേക്കും ആക്സസ് നേടുക"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ഓണാക്കുക"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"അടിയന്തരം"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"നിങ്ങളുടെ ഔദ്യോഗിക ആപ്പുകളിലേക്കും കോളുകളിലേക്കും ആക്സസ് നേടുക"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ആപ്പ് ലഭ്യമല്ല"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ലഭ്യമല്ല"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 28fa200..0dc530d 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Ажлын аппуудыг асаах уу?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Ажлын аппууд болон мэдэгдлүүддээ хандах эрх аваарай"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Асаах"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Яаралтай тусламж"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Ажлын аппууд болон дуудлагууддаа хандах эрх аваарай"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апп боломжгүй байна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> яг одоо боломжгүй байна."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> боломжгүй байна"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index d0d9da3..1b899a5 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"कार्य अॅप्स सुरू करायची का?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"तुमची कार्य ॲप्स आणि सूचना यांचा अॅक्सेस मिळवा"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"सुरू करा"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"आणीबाणी"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"तुमची कार्य ॲप्स आणि कॉल यांचा अॅक्सेस मिळवा"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ॲप उपलब्ध नाही"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता उपलब्ध नाही."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध नाही"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 3c0675b..7419995 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Hidupkan apl kerja?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Dapatkan akses kepada apl kerja dan pemberitahuan anda"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Hidupkan"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Kecemasan"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Dapatkan akses kepada apl kerja dan panggilan anda"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Apl tidak tersedia"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia sekarang."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> tidak tersedia"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 9c1f5ff..8d78806 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"အလုပ်သုံးအက်ပ်များ ဖွင့်မလား။"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"သင့်အလုပ်သုံးအက်ပ်နှင့် အကြောင်းကြားချက်များသုံးခွင့် ရယူပါ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ဖွင့်ရန်"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"အရေးပေါ်"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"သင်၏ အလုပ်သုံးအက်ပ်နှင့် ခေါ်ဆိုမှုများကို ဝင်ခွင့်တောင်းဆိုပါ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"အက်ပ်ကို မရနိုင်ပါ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ယခု မရနိုင်ပါ။"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> မရနိုင်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 1d231fe..1835ceb 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Vil du slå på jobbapper?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Få tilgang til jobbapper og -varsler"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Slå på"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nød"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Få tilgang til jobbapper og -samtaler"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgjengelig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgjengelig for øyeblikket."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> er utilgjengelig"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 54c24b5..9492433 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -330,8 +330,8 @@
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"ट्याप गरिएका वस्तुहरू चर्को स्वरमा बोलिने छन् र इसाराहरूको प्रयोग गरेर स्क्रिनमा अन्वेषण गर्न सकिन्छ।"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"आफुले टाइप गरेको पाठको निरीक्षण गर्नुहोस्"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"व्यक्तिगत डेटा जस्तै क्रेडिट कार्ड नम्बरहरू र पासवर्डहरू समावेश गर्दछ।"</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"प्रदर्शन आवर्धन नियन्त्रण गर्नुहोस्"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"प्रदर्शनको जुम स्तर र स्थिति नियन्त्रण गर्नुहोस्।"</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"डिस्प्ले म्याग्निफिकेसन नियन्त्रण गर्नुहोस्"</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"डिस्प्लेको जुम लेबल र स्थिति नियन्त्रण गर्नुहोस्।"</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"इसाराहरू सम्बन्धी कार्य गर्नुहोस्"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"ट्याप, स्वाइप गर्न, थिच्न र अन्य इसाराहरू सम्बन्धी कार्य गर्न सक्छ"</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"फिंगरप्रिन्टका इसाराहरू"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"कामसम्बन्धी एपहरू अन गर्ने हो?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"कामसम्बन्धी एप चलाउने र सूचना प्राप्त गर्ने सुविधा अन गर्नुहोस्"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"अन गर्नुहोस्"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"आपत्कालीन"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"कामसम्बन्धी एप चलाउने र कल प्राप्त गर्ने सुविधा अन गर्नुहोस्"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"एप उपलब्ध छैन"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> अहिले उपलब्ध छैन।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध छैन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index da34559..8de5fe4 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -228,7 +228,7 @@
<string name="shutdown_progress" msgid="5017145516412657345">"Uitzetten…"</string>
<string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Je tablet wordt uitgezet."</string>
<string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"Je Android TV-apparaat wordt uitgezet."</string>
- <string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"Je horloge wordt uitgezet."</string>
+ <string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"Je smartwatch wordt uitgezet."</string>
<string name="shutdown_confirm" product="default" msgid="136816458966692315">"Je telefoon wordt uitgezet."</string>
<string name="shutdown_confirm_question" msgid="796151167261608447">"Wil je afsluiten?"</string>
<string name="reboot_safemode_title" msgid="5853949122655346734">"Opnieuw opstarten in veilige modus"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Werk-apps aanzetten?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Krijg toegang tot je werk-apps en meldingen"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aanzetten"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Noodgeval"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Krijg toegang tot je werk-apps en gesprekken"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is niet beschikbaar"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel niet beschikbaar."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> niet beschikbaar"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 5cb0b9d..2dc96a7 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"ୱାର୍କ ଆପ୍ସ ଚାଲୁ କରିବେ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"ଆପଣଙ୍କ ୱାର୍କ ଆପ୍ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ୍ ପାଆନ୍ତୁ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ଚାଲୁ କରନ୍ତୁ"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ଜରୁରୀକାଳୀନ"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"ଆପଣଙ୍କ ୱାର୍କ ଆପ୍ସ ଏବଂ କଲଗୁଡ଼ିକୁ ଆକ୍ସେସ ପାଆନ୍ତୁ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ନାହିଁ।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ଉପଲବ୍ଧ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index de05d46..a2f3b11 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -324,7 +324,7 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ਆਪਣੇ ਸਰੀਰ ਦੇ ਅਹਿਮ ਚਿੰਨ੍ਹਾਂ ਬਾਰੇ ਸੰਵੇਦਕ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"ਸੂਚਨਾਵਾਂ"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ਵਿੰਡੋ ਸਮੱਗਰੀ ਮੁੜ ਪ੍ਰਾਪਤ ਕਰਨਾ"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ਵਿੰਡੋ ਸਮੱਗਰੀ ਮੁੜ-ਪ੍ਰਾਪਤ ਕਰਨਾ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ਉਸ ਵਿੰਡੋ ਸਮੱਗਰੀ ਦੀ ਜਾਂਚ ਕਰੋ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ ਅੰਤਰਕਿਰਿਆ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"\'ਸਪੱਰਸ਼ ਰਾਹੀਂ ਪੜਚੋਲ ਕਰੋ\' ਚਾਲੂ ਕਰਨਾ"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"ਟੈਪ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਉੱਚੀ ਆਵਾਜ਼ ਵਿੱਚ ਬੋਲਿਆ ਜਾਵੇਗਾ ਅਤੇ ਸਕ੍ਰੀਨ ਦੀ ਸੰਕੇਤਾਂ ਦੀ ਵਰਤੋਂ ਨਾਲ ਪੜਚੋਲ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਚਾਲੂ ਕਰਨੀਆਂ ਹਨ?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"ਆਪਣੀਆਂ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ਚਾਲੂ ਕਰੋ"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ਐਮਰਜੈਂਸੀ"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"ਆਪਣੀਆਂ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਅਤੇ ਕਾਲਾਂ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 9e0269b..d0857f5 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Włączyć aplikacje służbowe?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Uzyskaj dostęp do służbowych aplikacji i powiadomień"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Włącz"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Połączenie alarmowe"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Uzyskaj dostęp do służbowych aplikacji i połączeń"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – brak dostępu"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index ce1f686..8c03ed9 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Ativar apps de trabalho?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Acesse seus apps e notificações de trabalho"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergência"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Acesse seus apps e ligações de trabalho"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
<string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 66bd560..fc91fb4 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Ativar as apps de trabalho?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Obtenha acesso às suas apps de trabalho e notificações"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergência"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Obtenha acesso às suas apps de trabalho e chamadas"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"A app não está disponível"</string>
<string name="app_blocked_message" msgid="542972921087873023">"De momento, a app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ce1f686..8c03ed9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Ativar apps de trabalho?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Acesse seus apps e notificações de trabalho"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergência"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Acesse seus apps e ligações de trabalho"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
<string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 9663982..77206a1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Activezi aplicațiile pentru lucru?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Obține acces la aplicațiile și notificările pentru lucru"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Activează"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgență"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Solicită acces la aplicațiile și apelurile pentru lucru"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplicația nu este disponibilă"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu este disponibilă momentan."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nu este disponibilă"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ba548b3..650fc71 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Включить рабочие приложения?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Вы получите доступ к рабочим приложениям и уведомлениям"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Включить"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Экстренный вызов"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Получите доступ к рабочим приложениям и звонкам."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Приложение недоступно"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" сейчас недоступно."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недоступно: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 0363b0d..d686d82 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"කාර්යාල යෙදු. ක්රියා. කරන්නද?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"ඔබගේ කාර්යාල යෙදුම් සහ දැනුම්දීම් වෙත ප්රවේශය ලබා ගන්න"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ක්රියාත්මක කරන්න"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"හදිසි අවස්ථාව"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"ඔබේ කාර්යාල යෙදුම් සහ ඇමතුම් වෙත ප්රවේශය ලබා ගන්න"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"යෙදුම ලබා ගත නොහැකිය"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> මේ දැන් ලබා ගත නොහැකිය."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> නොතිබේ"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index e84bc83..123b63c4 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -331,7 +331,7 @@
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Zapnúť funkciu Preskúmanie dotykom"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Po klepnutí na položku sa vysloví jej názov a obrazovku je možné preskúmať pomocou gest."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Sledovať zadávaný text"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Sledovanie zahŕňa osobné údaje ako sú čísla kreditných kariet a heslá."</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Sledovanie zahŕňa osobné údaje, ako sú čísla kreditných kariet a heslá."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Ovládať priblíženie obrazovky"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Ovládajte umiestnenie a úroveň priblíženia obrazovky."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Gestá"</string>
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Zapnúť pracovné aplikácie?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Získajte prístup k svojim pracovným aplikáciám a upozorneniam"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Zapnúť"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Zavolať na tiesňovú linku"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Získajte prístup k svojim pracovným aplikáciám a hovorom"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikácia nie je dostupná"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> nie je teraz dostupná."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nie je k dispozícii"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 52bef0b..ce19c52 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Vklop delovnih aplikacij?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Pridobite dostop do delovnih aplikacij in obvestil za delovni profil."</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Vklopi"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nujni primer"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Zagotovite si dostop do delovnih aplikacij in klicev."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija ni na voljo"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno ni na voljo."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"»<xliff:g id="ACTIVITY">%1$s</xliff:g>« ni na voljo"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index d691fd0..4964349 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -324,20 +324,20 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"qasu tek të dhënat e sensorëve rreth shenjave të tua jetësore"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Njoftimet"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"shfaq njoftimet"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Nxjerrë përmbajtjen e dritares"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Të nxjerrë përmbajtjen e dritares"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspekton përmbajtjen e dritares me të cilën po ndërvepron."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Aktivizojë funksionin \"Eksploro me prekje\""</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Të aktivizojë veçorinë \"Eksploro me prekje\""</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Artikujt e trokitur do të lexohen me zë të lartë dhe ekrani mund të eksplorohet duke përdorur gjestet."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Vëzhgojë tekstin që shkruan"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Të vëzhgojë tekstin që shkruan"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Përfshin të dhëna personale, si numrat e kartave të kreditit dhe fjalëkalimet."</string>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Kontrollo zmadhimin e ekranit"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Kontrollo nivelin dhe pozicionimin e zmadhimit të ekranit."</string>
+ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Të kontrollojë zmadhimin e ekranit"</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Kontrollon nivelin dhe pozicionimin e zmadhimit të ekranit."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Kryen gjeste"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Mund të trokasë, rrëshqasë, bashkojë gishtat dhe kryejë gjeste të tjera."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gjestet e gjurmës së gishtit"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Mund të regjistrojë gjestet e kryera në sensorin e gjurmës së gishtit të pajisjes."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Nxirr një pamje të ekranit"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Mund të nxirret një pamje e ekranit."</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Mund të nxjerrë një pamje e ekranit."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"çaktivizo ose modifiko shiritin e statusit"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Lejon aplikacionin të çaktivizojë shiritin e statusit dhe të heqë ikonat e sistemit."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"të bëhet shiriti i statusit"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Të aktivizohen aplikacionet e punës?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Merr qasje tek aplikacionet e punës dhe njoftimet e tua"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivizo"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgjenca"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Merr qasje tek aplikacionet e punës dhe telefonatat e tua"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacioni nuk ofrohet"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk ofrohet për momentin."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nuk ofrohet"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index aab16eb..914e36c 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1944,10 +1944,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Укључујете пословне апликације?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Приступајте пословним апликацијама и обавештењима"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Укључи"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Хитан случај"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Приступајте пословним апликацијама и позивима"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апликација није доступна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> тренутно није доступна."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – није доступно"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 125999b..c45c847 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -337,7 +337,7 @@
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Fingeravtrycksrörelser"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan registrera rörelser som utförs med hjälp av enhetens fingeravtryckssensor."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ta skärmbild"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Du kan ta en skärmbild av skärmen."</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan ta en skärmbild av skärmen."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"inaktivera eller ändra statusfält"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"visas i statusfältet"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Vill du aktivera jobbappar?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Få åtkomst till jobbappar och aviseringar"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivera"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nödsituation"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Få åtkomst till jobbappar och samtal"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen är inte tillgänglig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> är inte tillgängligt just nu."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> är inte tillgänglig"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 5cf2f77..cc7aa19 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Iwashe programu za kazini?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Pata uwezo wa kufikia arifa na programu zako za kazini"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Washa"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Simu za dharura"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Pata uwezo wa kufikia simu na programu zako za kazini"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Programu haipatikani"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> haipatikani hivi sasa."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> haipatikani"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 8f576ab..149f895 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -328,7 +328,7 @@
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"நீங்கள் பணியாற்றிக் கொண்டிருக்கும் சாளரத்தின் உள்ளடக்கத்தைப் பார்க்கலாம்."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"தொடுவதன் மூலம் அறிவதை இயக்கும்"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"தட்டிய உள்ளடக்கம் சத்தமாகப் படிக்கப்படும், சைகைகளைப் பயன்படுத்தி திரையில் உலாவலாம்."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"நீங்கள் தட்டச்சு செய்யும் உரையைக் கவனிக்கும்"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"நீங்கள் டைப் செய்யும் வார்த்தையைக் கவனிக்கும்."</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"கிரெடிட் கார்டு எண்கள் மற்றும் கடவுச்சொற்கள் போன்ற தனிப்பட்ட தகவலும் உள்ளடங்கும்."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"திரை பெரிதாவதைக் கட்டுப்படுத்தும்"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"திரையின் ஜூம் அளவையும் நிலையையும் கட்டுப்படுத்தலாம்."</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"பணி ஆப்ஸை இயக்கவா?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"உங்கள் பணி ஆப்ஸுக்கும் அறிவிப்புகளுக்குமான அணுகலைப் பெறுங்கள்"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"இயக்கு"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"அவசர அழைப்பு"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"உங்கள் பணி ஆப்ஸுக்கும் அழைப்புகளுக்குமான அணுகலைப் பெறுங்கள்"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"இந்த ஆப்ஸ் இப்போது கிடைப்பதில்லை"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இப்போது கிடைப்பதில்லை."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> இல்லை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 3cf745b..709be1a 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -326,7 +326,7 @@
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"నోటిఫికేషన్లను చూపండి"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"విండో కంటెంట్ను తిరిగి పొందుతుంది"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"మీరు పరస్పర చర్య చేస్తున్న విండో కంటెంట్ను పరిశీలిస్తుంది."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"తాకడం ద్వారా విశ్లేషణను ప్రారంభిస్తుంది"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"తాకడం ద్వారా విశ్లేషణను ఆన్ చేయండి"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"నొక్కిన అంశాలు బిగ్గరగా చదివి వినిపించబడతాయి మరియు సంజ్ఞలను ఉపయోగించి స్క్రీన్ను విశ్లేషించవచ్చు."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"మీరు టైప్ చేస్తున్న వచనాన్ని పరిశీలిస్తుంది"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"క్రెడిట్ కార్డు నంబర్లు మరియు పాస్వర్డ్ల వంటి వ్యక్తిగత డేటాను కలిగి ఉంటుంది."</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"వర్క్ యాప్లను ఆన్ చేయాలా?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"మీ వర్క్ యాప్లు, నోటిఫికేషన్లకు యాక్సెస్ను పొందండి"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"ఆన్ చేయి"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ఎమర్జెన్సీ"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"మీ వర్క్ యాప్లు, కాల్స్కు యాక్సెస్ను పొందండి"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"యాప్ అందుబాటులో లేదు"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> అందుబాటులో లేదు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4afe454..3d46e4d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"เปิดแอปงานใช่ไหม"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"รับสิทธิ์เข้าถึงแอปงานและการแจ้งเตือนต่างๆ"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"เปิด"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ฉุกเฉิน"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"รับสิทธิ์เข้าถึงแอปงานและการโทร"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"แอปไม่พร้อมใช้งาน"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่พร้อมใช้งานในขณะนี้"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ไม่พร้อมใช้งาน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e808d56..ff08592 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"I-on ang app para sa trabaho?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Makakuha ng access sa iyong mga app para sa trabaho at notification"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"I-on"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Makakuha ng access sa iyong mga app para sa trabaho at tawag"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Hindi available ang app"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Hindi available sa ngayon ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Hindi available ang <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 5d471b3..79ba5b5 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"İş uygulamaları açılsın mı?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"İş uygulamalarınıza ve bildirimlerinize erişin"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Aç"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Acil durum"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"İş uygulamalarınıza ve telefon aramalarınıza erişin"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Uygulama kullanılamıyor"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması şu anda kullanılamıyor."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> kullanılamıyor"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 1620c8f..cd8762f 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -329,7 +329,7 @@
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Отримувати вміст вікна"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Перевіряти вміст вікна, з яким ви взаємодієте."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Увімкнути функцію дослідження дотиком"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Активувати голосові підказки для елементів, яких торкаються, і користуватися інтерфейсом за допомогою жестів."</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Озвучувати елементи, яких торкаються, і здійснювати навігацію екраном за допомогою жестів."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Переглядати текст, який ви вводите"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Включає особисті дані, як-от номери кредитних карток і паролі."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Контролювати збільшення екрана"</string>
@@ -1945,10 +1945,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Увімкнути робочі додатки?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Отримайте доступ до своїх робочих додатків і сповіщень"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Увімкнути"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Екстрений виклик"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Отримайте доступ до своїх робочих додатків і дзвінків"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Додаток недоступний"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> зараз недоступний."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недоступно: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2b5b2f1..88cf716 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"ورک ایپس آن کریں؟"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"اپنی ورک ایپس اور اطلاعات تک رسائی حاصل کریں"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"آن کریں"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ایمرجنسی"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"اپنی ورک ایپس اور کالز تک رسائی حاصل کریں"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> دستیاب نہیں ہے"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 258d4ce..eeb799f 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -326,7 +326,7 @@
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"hiển thị thông báo"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Truy xuất nội dung cửa sổ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Kiểm tra nội dung của cửa sổ bạn đang tương tác."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Bật Khám phá bằng cách chạm"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Bật tính năng Khám phá bằng cách chạm"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Đọc to các mục được nhấn và cho phép khám phá màn hình bằng cử chỉ."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Quan sát nội dung bạn nhập"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Bao gồm dữ liệu cá nhân chẳng hạn như số thẻ tín dụng và mật khẩu."</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Bật các ứng dụng công việc?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Bạn sẽ có quyền truy cập vào các ứng dụng công việc và thông báo"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Bật"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Khẩn cấp"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Hãy lấy quyền cập vào ứng dụng công việc và cuộc gọi"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Ứng dụng này không dùng được"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện không dùng được."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Không hỗ trợ <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 9b998c1..820d692 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"要开启工作应用访问权限吗?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"获取工作应用和通知的访问权限"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"开启"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"紧急呼叫"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"获取工作应用和通话的访问权限"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"应用无法使用"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前无法使用。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>不可用"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 065968c..b0e5f60 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"要開啟工作應用程式存取權嗎?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"開啟工作應用程式和通知的存取權"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"撥打緊急電話"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"要求存取工作應用程式和通話"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"無法使用應用程式"</string>
<string name="app_blocked_message" msgid="542972921087873023">"目前無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"無法使用「<xliff:g id="ACTIVITY">%1$s</xliff:g>」"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index fd94101..79b8bce 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -280,7 +280,7 @@
<string name="notification_channel_vpn" msgid="1628529026203808999">"VPN 狀態"</string>
<string name="notification_channel_device_admin" msgid="6384932669406095506">"來自 IT 管理員的快訊"</string>
<string name="notification_channel_alerts" msgid="5070241039583668427">"快訊"</string>
- <string name="notification_channel_retail_mode" msgid="3732239154256431213">"零售商示範模式"</string>
+ <string name="notification_channel_retail_mode" msgid="3732239154256431213">"零售商展示模式"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"USB 連線"</string>
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"應用程式執行中"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"正在耗用電量的應用程式"</string>
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"要開啟工作應用程式存取權嗎?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"開啟工作應用程式和通知的存取權"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"撥打緊急電話"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"要求存取工作應用程式和通話"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"應用程式無法使用"</string>
<string name="app_blocked_message" msgid="542972921087873023">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前無法使用。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"無法存取「<xliff:g id="ACTIVITY">%1$s</xliff:g>」"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 2d17ae2..55b5e9a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1943,10 +1943,8 @@
<string name="work_mode_off_title" msgid="961171256005852058">"Vula ama-app okusebenza womsebenzi?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Thola ukufinyelela kuma-app akho womsebenzi kanye nezaziso"</string>
<string name="work_mode_turn_on" msgid="3662561662475962285">"Vula"</string>
- <!-- no translation found for work_mode_emergency_call_button (6818855962881612322) -->
- <skip />
- <!-- no translation found for work_mode_dialer_off_message (2193299184850387465) -->
- <skip />
+ <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Isimo esiphuthumayo"</string>
+ <string name="work_mode_dialer_off_message" msgid="2193299184850387465">"Thola ukufinyelela kuma-app akho womsebenzi kanye namakholi"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Uhlelo lokusebenza alutholakali"</string>
<string name="app_blocked_message" msgid="542972921087873023">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayitholakali khona manje."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"okungatholakali <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 4b27bf2..fe296c7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -18,7 +18,6 @@
<bool name="kg_enable_camera_default_widget">true</bool>
<bool name="kg_center_small_widgets_vertically">false</bool>
<bool name="kg_top_align_page_shrink_on_bouncer_visible">true</bool>
- <bool name="kg_wake_on_acquire_start">false</bool>
<bool name="action_bar_embed_tabs">true</bool>
<bool name="split_action_bar_is_narrow">true</bool>
<bool name="preferences_prefer_dual_pane">false</bool>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index fa77c45..e242e20 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -435,6 +435,119 @@
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_1000">#000000</color>
+ <!-- Colors used in Android system, from Material design system.
+ These values can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_container_light">#D8E2FF</color>
+ <color name="system_on_primary_container_light">#001A41</color>
+ <color name="system_primary_light">#445E91</color>
+ <color name="system_on_primary_light">#FFFFFF</color>
+ <color name="system_secondary_container_light">#DBE2F9</color>
+ <color name="system_on_secondary_container_light">#141B2C</color>
+ <color name="system_secondary_light">#575E71</color>
+ <color name="system_on_secondary_light">#FFFFFF</color>
+ <color name="system_tertiary_container_light">#FBD7FC</color>
+ <color name="system_on_tertiary_container_light">#29132D</color>
+ <color name="system_tertiary_light">#715573</color>
+ <color name="system_on_tertiary_light">#FFFFFF</color>
+ <color name="system_background_light">#FAF9FD</color>
+ <color name="system_on_background_light">#1B1B1F</color>
+ <color name="system_surface_light">#FAF9FD</color>
+ <color name="system_on_surface_light">#1B1B1F</color>
+ <color name="system_surface_container_low_light">#F5F3F7</color>
+ <color name="system_surface_container_lowest_light">#FFFFFF</color>
+ <color name="system_surface_container_light">#EFEDF1</color>
+ <color name="system_surface_container_high_light">#E9E7EC</color>
+ <color name="system_surface_container_highest_light">#E3E2E6</color>
+ <color name="system_surface_bright_light">#FAF9FD</color>
+ <color name="system_surface_dim_light">#DBD9DD</color>
+ <color name="system_surface_variant_light">#E1E2EC</color>
+ <color name="system_on_surface_variant_light">#44474F</color>
+ <color name="system_outline_light">#72747D</color>
+ <color name="system_error_light">#C00003</color>
+ <color name="system_on_error_light">#FFFFFF</color>
+ <color name="system_error_container_light">#FFDAD5</color>
+ <color name="system_on_error_container_light">#410000</color>
+ <color name="system_primary_fixed_light">#D8E2FF</color>
+ <color name="system_primary_fixed_darker_light">#ADC6FF</color>
+ <color name="system_on_primary_fixed_light">#001A41</color>
+ <color name="system_on_primary_fixed_variant_light">#2B4678</color>
+ <color name="system_secondary_fixed_light">#DBE2F9</color>
+ <color name="system_secondary_fixed_darker_light">#BFC6DC</color>
+ <color name="system_on_secondary_fixed_light">#141B2C</color>
+ <color name="system_on_secondary_fixed_variant_light">#3F4759</color>
+ <color name="system_tertiary_fixed_light">#FBD7FC</color>
+ <color name="system_tertiary_fixed_darker_light">#DEBCDF</color>
+ <color name="system_on_tertiary_fixed_light">#29132D</color>
+ <color name="system_on_tertiary_fixed_variant_light">#583E5B</color>
+ <color name="system_control_activated_light">#D8E2FF</color>
+ <color name="system_control_normal_light">#44474F</color>
+ <color name="system_control_highlight_light">#1F000000</color>
+ <color name="system_text_primary_inverse_light">#E3E2E6</color>
+ <color name="system_text_secondary_and_tertiary_inverse_light">#C4C6D0</color>
+ <color name="system_text_primary_inverse_disable_only_light">#E3E2E6</color>
+ <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E3E2E6</color>
+ <color name="system_text_hint_inverse_light">#E3E2E6</color>
+ <color name="system_palette_key_color_primary_light">#5D77AC</color>
+ <color name="system_palette_key_color_secondary_light">#6C7488</color>
+ <color name="system_palette_key_color_tertiary_light">#907292</color>
+ <color name="system_palette_key_color_neutral_light">#838387</color>
+ <color name="system_palette_key_color_neutral_variant_light">#777982</color>
+ <color name="system_primary_container_dark">#2B4678</color>
+ <color name="system_on_primary_container_dark">#D8E2FF</color>
+ <color name="system_primary_dark">#ADC6FF</color>
+ <color name="system_on_primary_dark">#102F60</color>
+ <color name="system_secondary_container_dark">#3F4759</color>
+ <color name="system_on_secondary_container_dark">#DBE2F9</color>
+ <color name="system_secondary_dark">#BFC6DC</color>
+ <color name="system_on_secondary_dark">#293041</color>
+ <color name="system_tertiary_container_dark">#583E5B</color>
+ <color name="system_on_tertiary_container_dark">#FBD7FC</color>
+ <color name="system_tertiary_dark">#DEBCDF</color>
+ <color name="system_on_tertiary_dark">#402843</color>
+ <color name="system_background_dark">#121316</color>
+ <color name="system_on_background_dark">#E3E2E6</color>
+ <color name="system_surface_dark">#121316</color>
+ <color name="system_on_surface_dark">#E3E2E6</color>
+ <color name="system_surface_container_low_dark">#1B1B1F</color>
+ <color name="system_surface_container_lowest_dark">#0D0E11</color>
+ <color name="system_surface_container_dark">#1F1F23</color>
+ <color name="system_surface_container_high_dark">#292A2D</color>
+ <color name="system_surface_container_highest_dark">#343538</color>
+ <color name="system_surface_bright_dark">#38393C</color>
+ <color name="system_surface_dim_dark">#121316</color>
+ <color name="system_surface_variant_dark">#44474F</color>
+ <color name="system_on_surface_variant_dark">#C4C6D0</color>
+ <color name="system_outline_dark">#72747D</color>
+ <color name="system_error_dark">#FFB4A8</color>
+ <color name="system_on_error_dark">#690001</color>
+ <color name="system_error_container_dark">#930001</color>
+ <color name="system_on_error_container_dark">#FFDAD5</color>
+ <color name="system_primary_fixed_dark">#D8E2FF</color>
+ <color name="system_primary_fixeder_dark">#ADC6FF</color>
+ <color name="system_on_primary_fixed_dark">#001A41</color>
+ <color name="system_on_primary_fixed_variant_dark">#2B4678</color>
+ <color name="system_secondary_fixed_dark">#DBE2F9</color>
+ <color name="system_secondary_fixeder_dark">#BFC6DC</color>
+ <color name="system_on_secondary_fixed_dark">#141B2C</color>
+ <color name="system_on_secondary_fixed_variant_dark">#3F4759</color>
+ <color name="system_tertiary_fixed_dark">#FBD7FC</color>
+ <color name="system_tertiary_fixeder_dark">#DEBCDF</color>
+ <color name="system_on_tertiary_fixed_dark">#29132D</color>
+ <color name="system_on_tertiary_fixed_variant_dark">#583E5B</color>
+ <color name="system_control_activated_dark">#2B4678</color>
+ <color name="system_control_normal_dark">#C4C6D0</color>
+ <color name="system_control_highlight_dark">#33FFFFFF</color>
+ <color name="system_text_primary_inverse_dark">#1B1B1F</color>
+ <color name="system_text_secondary_and_tertiary_inverse_dark">#44474F</color>
+ <color name="system_text_primary_inverse_disable_only_dark">#1B1B1F</color>
+ <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1B1B1F</color>
+ <color name="system_text_hint_inverse_dark">#1B1B1F</color>
+ <color name="system_palette_key_color_primary_dark">#5D77AC</color>
+ <color name="system_palette_key_color_secondary_dark">#6C7488</color>
+ <color name="system_palette_key_color_tertiary_dark">#907292</color>
+ <color name="system_palette_key_color_neutral_dark">#838387</color>
+ <color name="system_palette_key_color_neutral_variant_dark">#777982</color>
+
<!-- Accessibility shortcut icon background color -->
<color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
<color name="accessibility_magnification_background">#F50D60</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index be22095..d3aee43 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -465,6 +465,12 @@
<string-array translatable="false" name="config_cdma_dun_supported_types">
</string-array>
+ <!-- Package name of the system app that implements the shared connectivity service -->
+ <string translatable="false" name="sharedconnectivity_service_package"></string>
+
+ <!-- Class name in the system app that implements the shared connectivity service -->
+ <string translatable="false" name="sharedconnectivity_service_class"></string>
+
<!-- Flag indicating whether we should enable the automatic brightness.
Software implementation will be used if config_hardware_auto_brightness_available is not set -->
<bool name="config_automatic_brightness_available">false</bool>
@@ -636,6 +642,16 @@
The default is false. -->
<bool name="config_lidControlsSleep">false</bool>
+ <!-- The device states (supplied by DeviceStateManager) that should be treated as open by the
+ device fold controller. Default is empty. -->
+ <integer-array name="config_openDeviceStates">
+ <!-- Example:
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ -->
+ </integer-array>
+
<!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
display fold controller. Default is empty. -->
<integer-array name="config_foldedDeviceStates">
@@ -656,6 +672,16 @@
-->
</integer-array>
+ <!-- The device states (supplied by DeviceStateManager) that should be treated as a rear display
+ state. Default is empty. -->
+ <integer-array name="config_rearDisplayDeviceStates">
+ <!-- Example:
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ -->
+ </integer-array>
+
<!-- Indicates whether the window manager reacts to half-fold device states by overriding
rotation. -->
<bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
@@ -948,6 +974,12 @@
<!-- Boolean indicating whether light mode is allowed when DWB is turned on. -->
<bool name="config_displayWhiteBalanceLightModeAllowed">true</bool>
+ <!-- Device states where the sensor based rotation values should be reversed around the Z axis
+ for the default display.
+ TODO(b/265312193): Remove this workaround when this bug is fixed.-->
+ <integer-array name="config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis">
+ </integer-array>
+
<!-- Indicate available ColorDisplayManager.COLOR_MODE_xxx. -->
<integer-array name="config_availableColorModes">
<!-- Example:
@@ -4601,6 +4633,12 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
+ <!-- Package name for the default Health Connect app.
+ OEMs can set this with their own health app package name to define a default app with high
+ priority for the app to store the health data. If set the app always has priority of 1
+ unless it is changed by the user. -->
+ <string name="config_defaultHealthConnectApp" translatable="false"></string>
+
<!-- This is the default launcher package with an activity to use on secondary displays that
support system decorations.
This launcher package must have an activity that supports multiple instances and has
@@ -5808,7 +5846,6 @@
- config_roundedCornerDrawableArray (config in SystemUI resource)
- config_roundedCornerTopDrawableArray (config in SystemUI resource)
- config_roundedCornerBottomDrawableArray (config in SystemUI resource)
- - config_displayUsiVersionArray
Leave this array empty for single display device and the system will load the default main
built-in related configs.
@@ -6166,26 +6203,15 @@
<item>@string/config_mainDisplayShape</item>
<item>@string/config_secondaryDisplayShape</item>
</string-array>
+
+ <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner
+ permission for staging HealthConnect's remote data. The digest should be computed over the
+ DER encoding of the trusted certificate using the SHA-256 digest algorithm. -->
+ <string-array name="config_healthConnectStagingDataKnownSigners">
+ </string-array>
<!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner Health
Connect Migration permissions. The digest should be computed over the DER encoding of the
trusted certificate using the SHA-256 digest algorithm. -->
<string-array name="config_healthConnectMigrationKnownSigners">
</string-array>
-
- <!-- The Universal Stylus Initiative (USI) protocol version supported by each display.
- (@see https://universalstylus.org/).
-
- The i-th value in this array corresponds to the supported USI version of the i-th display
- listed in config_displayUniqueIdArray. On a single-display device, the
- config_displayUniqueIdArray may be empty, in which case the only value in this array should
- be the USI version for the main built-in display.
-
- If the display does not support USI, the version value should be an empty string. If the
- display supports USI, the version must be in the following format:
- "<major-version>.<minor-version>"
-
- For example, "", "1.0", and "2.0" are valid values. -->
- <string-array name="config_displayUsiVersionArray" translatable="false">
- <item>""</item>
- </string-array>
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 925b5d3..d40adf5 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -140,10 +140,4 @@
only single logical modem, by using its data connection in addition to cellular IMS. -->
<bool name="config_enable_virtual_dsda">false</bool>
<java-symbol type="bool" name="config_enable_virtual_dsda" />
-
- <!-- Whether to enable getSubscriptionUserHandle() api.
- If the value is true, return user handle associated with the subscription.
- If the value is set to false, return null. -->
- <bool name="config_enable_get_subscription_user_handle">true</bool>
- <java-symbol type="bool" name="config_enable_get_subscription_user_handle" />
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 58b8601..f7ed38c 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -154,6 +154,116 @@
</staging-public-group>
<staging-public-group type="color" first-id="0x01c90000">
+ <public name="system_primary_container_light" />
+ <public name="system_on_primary_container_light" />
+ <public name="system_primary_light" />
+ <public name="system_on_primary_light" />
+ <public name="system_secondary_container_light" />
+ <public name="system_on_secondary_container_light" />
+ <public name="system_secondary_light" />
+ <public name="system_on_secondary_light" />
+ <public name="system_tertiary_container_light" />
+ <public name="system_on_tertiary_container_light" />
+ <public name="system_tertiary_light" />
+ <public name="system_on_tertiary_light" />
+ <public name="system_background_light" />
+ <public name="system_on_background_light" />
+ <public name="system_surface_light" />
+ <public name="system_on_surface_light" />
+ <public name="system_surface_container_low_light" />
+ <public name="system_surface_container_lowest_light" />
+ <public name="system_surface_container_light" />
+ <public name="system_surface_container_high_light" />
+ <public name="system_surface_container_highest_light" />
+ <public name="system_surface_bright_light" />
+ <public name="system_surface_dim_light" />
+ <public name="system_surface_variant_light" />
+ <public name="system_on_surface_variant_light" />
+ <public name="system_outline_light" />
+ <public name="system_error_light" />
+ <public name="system_on_error_light" />
+ <public name="system_error_container_light" />
+ <public name="system_on_error_container_light" />
+ <public name="system_primary_fixed_light" />
+ <public name="system_primary_fixed_darker_light" />
+ <public name="system_on_primary_fixed_light" />
+ <public name="system_on_primary_fixed_variant_light" />
+ <public name="system_secondary_fixed_light" />
+ <public name="system_secondary_fixed_darker_light" />
+ <public name="system_on_secondary_fixed_light" />
+ <public name="system_on_secondary_fixed_variant_light" />
+ <public name="system_tertiary_fixed_light" />
+ <public name="system_tertiary_fixed_darker_light" />
+ <public name="system_on_tertiary_fixed_light" />
+ <public name="system_on_tertiary_fixed_variant_light" />
+ <public name="system_control_activated_light" />
+ <public name="system_control_normal_light" />
+ <public name="system_control_highlight_light" />
+ <public name="system_text_primary_inverse_light" />
+ <public name="system_text_secondary_and_tertiary_inverse_light" />
+ <public name="system_text_primary_inverse_disable_only_light" />
+ <public name="system_text_secondary_and_tertiary_inverse_disabled_light" />
+ <public name="system_text_hint_inverse_light" />
+ <public name="system_palette_key_color_primary_light" />
+ <public name="system_palette_key_color_secondary_light" />
+ <public name="system_palette_key_color_tertiary_light" />
+ <public name="system_palette_key_color_neutral_light" />
+ <public name="system_palette_key_color_neutral_variant_light" />
+ <public name="system_primary_container_dark"/>
+ <public name="system_on_primary_container_dark"/>
+ <public name="system_primary_dark"/>
+ <public name="system_on_primary_dark"/>
+ <public name="system_secondary_container_dark"/>
+ <public name="system_on_secondary_container_dark"/>
+ <public name="system_secondary_dark"/>
+ <public name="system_on_secondary_dark"/>
+ <public name="system_tertiary_container_dark"/>
+ <public name="system_on_tertiary_container_dark"/>
+ <public name="system_tertiary_dark"/>
+ <public name="system_on_tertiary_dark"/>
+ <public name="system_background_dark"/>
+ <public name="system_on_background_dark"/>
+ <public name="system_surface_dark"/>
+ <public name="system_on_surface_dark"/>
+ <public name="system_surface_container_low_dark"/>
+ <public name="system_surface_container_lowest_dark"/>
+ <public name="system_surface_container_dark"/>
+ <public name="system_surface_container_high_dark"/>
+ <public name="system_surface_container_highest_dark"/>
+ <public name="system_surface_bright_dark"/>
+ <public name="system_surface_dim_dark"/>
+ <public name="system_surface_variant_dark"/>
+ <public name="system_on_surface_variant_dark"/>
+ <public name="system_outline_dark"/>
+ <public name="system_error_dark"/>
+ <public name="system_on_error_dark"/>
+ <public name="system_error_container_dark"/>
+ <public name="system_on_error_container_dark"/>
+ <public name="system_primary_fixed_dark"/>
+ <public name="system_primary_fixeder_dark"/>
+ <public name="system_on_primary_fixed_dark"/>
+ <public name="system_on_primary_fixed_variant_dark"/>
+ <public name="system_secondary_fixed_dark"/>
+ <public name="system_secondary_fixeder_dark"/>
+ <public name="system_on_secondary_fixed_dark"/>
+ <public name="system_on_secondary_fixed_variant_dark"/>
+ <public name="system_tertiary_fixed_dark"/>
+ <public name="system_tertiary_fixeder_dark"/>
+ <public name="system_on_tertiary_fixed_dark"/>
+ <public name="system_on_tertiary_fixed_variant_dark"/>
+ <public name="system_control_activated_dark"/>
+ <public name="system_control_normal_dark"/>
+ <public name="system_control_highlight_dark"/>
+ <public name="system_text_primary_inverse_dark"/>
+ <public name="system_text_secondary_and_tertiary_inverse_dark"/>
+ <public name="system_text_primary_inverse_disable_only_dark"/>
+ <public name="system_text_secondary_and_tertiary_inverse_disabled_dark"/>
+ <public name="system_text_hint_inverse_dark"/>
+ <public name="system_palette_key_color_primary_dark"/>
+ <public name="system_palette_key_color_secondary_dark"/>
+ <public name="system_palette_key_color_tertiary_dark"/>
+ <public name="system_palette_key_color_neutral_dark"/>
+ <public name="system_palette_key_color_neutral_variant_dark"/>
</staging-public-group>
<staging-public-group type="array" first-id="0x01c80000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7c6f81d..e330ee3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1329,6 +1329,16 @@
<!-- Description of the background body sensors permission, listed so the user can decide whether to allow the application to access data from body sensors in the background. [CHAR LIMIT=NONE] -->
<string name="permdesc_bodySensors_background" product="default">Allows the app to access body sensor data, such as heart rate, temperature, and blood oxygen percentage, while the app is in the background.</string>
+ <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
+ <string name="permlab_bodySensorsWristTemperature">Access body sensor wrist temperature data while the app is in use.</string>
+ <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bodySensorsWristTemperature" product="default">Allows the app to access body sensor wrist temperature data, while the app is in use.</string>
+
+ <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
+ <string name="permlab_bodySensors_wristTemperature_background">Access body sensor wrist temperature data while the app is in the background.</string>
+ <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bodySensors_wristTemperature_background" product="default">Allows the app to access body sensor wrist temperature data, while the app is in the background.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readCalendar">Read calendar events and details</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -5014,6 +5024,14 @@
<!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] -->
<string name="immersive_cling_positive">Got it</string>
+ <!-- Text on a toast shown after the system rotates the screen for camera app
+ compatibility. [CHAR LIMIT=NONE] -->
+ <string name="display_rotation_camera_compat_toast_after_rotation">Rotate for a better view</string>
+
+ <!-- Text on a toast shown when a camera view is started within the app that may not be able
+ to display the camera preview correctly while in split screen. [CHAR LIMIT=NONE] -->
+ <string name="display_rotation_camera_compat_toast_in_split_screen">Exit split screen for a better view</string>
+
<!-- Label for button to confirm chosen date or time [CHAR LIMIT=30] -->
<string name="done_label">Done</string>
<!--
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2715c94..f1b8cca 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -341,6 +341,7 @@
<java-symbol type="bool" name="config_checkWallpaperAtBoot" />
<java-symbol type="string" name="config_wallpaperManagerServiceName" />
<java-symbol type="string" name="config_inputEventCompatProcessorOverrideClassName" />
+ <java-symbol type="string" name="config_defaultHealthConnectApp" />
<java-symbol type="bool" name="config_sendAudioBecomingNoisy" />
<java-symbol type="bool" name="config_enableScreenshotChord" />
<java-symbol type="bool" name="config_enableWifiDisplay" />
@@ -2773,7 +2774,6 @@
<java-symbol type="dimen" name="fast_scroller_minimum_touch_target" />
<java-symbol type="array" name="config_cdma_international_roaming_indicators" />
<java-symbol type="string" name="kg_text_message_separator" />
- <java-symbol type="bool" name="kg_wake_on_acquire_start" />
<java-symbol type="bool" name="config_use_sim_language_file" />
<java-symbol type="bool" name="config_LTE_eri_for_network_name" />
@@ -3385,6 +3385,11 @@
<java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" />
<java-symbol type="bool" name="config_displayWhiteBalanceLightModeAllowed" />
+ <!-- Device states where the sensor based rotation values should be reversed around the Z axis
+ for the default display.
+ TODO(b/265312193): Remove this workaround when this bug is fixed.-->
+ <java-symbol type="array" name="config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis" />
+
<!-- Default user restrictions for the SYSTEM user -->
<java-symbol type="array" name="config_defaultFirstUserRestrictions" />
@@ -3960,8 +3965,10 @@
<java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />
<!-- For Foldables -->
+ <java-symbol type="array" name="config_openDeviceStates" />
<java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="array" name="config_halfFoldedDeviceStates" />
+ <java-symbol type="array" name="config_rearDisplayDeviceStates" />
<java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
<java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
@@ -4781,7 +4788,6 @@
<java-symbol type="array" name="config_mainBuiltInDisplayWaterfallCutout" />
<java-symbol type="array" name="config_secondaryBuiltInDisplayWaterfallCutout" />
<java-symbol type="array" name="config_waterfallCutoutArray" />
- <java-symbol type="array" name="config_displayUsiVersionArray" />
<java-symbol type="fraction" name="global_actions_vertical_padding_percentage" />
<java-symbol type="fraction" name="global_actions_horizontal_padding_percentage" />
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
index f807bad..75f8c95 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
@@ -22,19 +22,22 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.ArraySet;
-import androidx.test.InstrumentationRegistry;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -48,10 +51,11 @@
@RunWith(MockitoJUnitRunner.class)
public final class ProgramListTest {
- public final Context mContext = InstrumentationRegistry.getContext();
+ private static final int TEST_TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
private static final int CREATOR_ARRAY_SIZE = 3;
- private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(/* millis= */ 500);
+ private static final int TIMEOUT_MS = 500;
+ private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(TIMEOUT_MS);
private static final boolean IS_PURGE = false;
private static final boolean IS_COMPLETE = true;
@@ -95,6 +99,7 @@
command.run();
}
};
+ private final ApplicationInfo mApplicationInfo = new ApplicationInfo();
private RadioTuner mRadioTuner;
private ITunerCallback mTunerCallback;
@@ -103,6 +108,8 @@
private ProgramList.ListCallback[] mListCallbackMocks;
private ProgramList.OnCompleteListener[] mOnCompleteListenerMocks;
@Mock
+ private Context mContextMock;
+ @Mock
private IRadioService mRadioServiceMock;
@Mock
private ITuner mTunerMock;
@@ -268,6 +275,18 @@
}
@Test
+ public void getProgramList_forTunerAdapterWhenListNotReady_fails() throws Exception {
+ Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
+ createRadioTuner();
+
+ IllegalStateException thrown = assertThrows(IllegalStateException.class,
+ () -> mRadioTuner.getProgramList(parameters));
+
+ assertWithMessage("Exception for getting program list when not ready")
+ .that(thrown).hasMessageThat().contains("Program list is not ready yet");
+ }
+
+ @Test
public void getProgramList_forTunerAdapterWhenServiceDied_fails() throws Exception {
Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
createRadioTuner();
@@ -290,6 +309,19 @@
}
@Test
+ public void getDynamicProgramList_forTunerNotSupportingProgramList_returnsNull()
+ throws Exception {
+ createRadioTuner();
+ doThrow(new UnsupportedOperationException())
+ .when(mTunerMock).startProgramListUpdates(any());
+
+ ProgramList nullProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+
+ assertWithMessage("Exception for radio HAL client not supporting program list")
+ .that(nullProgramList).isNull();
+ }
+
+ @Test
public void getDynamicProgramList_forTunerAdapterWithServiceDied_throwsException()
throws Exception {
createRadioTuner();
@@ -346,7 +378,7 @@
mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
- verify(mOnCompleteListenerMocks[0], CALLBACK_TIMEOUT.times(0)).onComplete();
+ verify(mOnCompleteListenerMocks[0], after(TIMEOUT_MS).never()).onComplete();
}
@Test
@@ -367,6 +399,22 @@
}
@Test
+ public void onProgramListUpdated_afterProgramListClosed_notInvokeMockedCallbacks()
+ throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ registerListCallbacks(/* numCallbacks= */ 1);
+ addOnCompleteListeners(/* numListeners= */ 1);
+ mProgramList.close();
+
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ verify(mListCallbackMocks[0], after(TIMEOUT_MS).never()).onItemChanged(any());
+ verify(mListCallbackMocks[0], never()).onItemChanged(any());
+ verify(mOnCompleteListenerMocks[0], never()).onComplete();
+ }
+
+ @Test
public void onItemChanged_forListCallbackRegisteredWithExecutor_invokesWhenIdAdded()
throws Exception {
createRadioTuner();
@@ -410,6 +458,41 @@
}
@Test
+ public void onBackgroundScanComplete_whenProgramListReady_invokesMockedCallback()
+ throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ mTunerCallback.onBackgroundScanComplete();
+
+ verify(mTunerCallbackMock, CALLBACK_TIMEOUT).onBackgroundScanComplete();
+ }
+
+ @Test
+ public void onBackgroundScanComplete_whenProgramListNotReady_notInvokeMockedCallback()
+ throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+
+ mTunerCallback.onBackgroundScanComplete();
+
+ verify(mTunerCallbackMock, after(TIMEOUT_MS).never()).onBackgroundScanComplete();
+ }
+
+ @Test
+ public void onBackgroundScanComplete_afterProgramListReady_invokesMockedCallback()
+ throws Exception {
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+
+ mTunerCallback.onBackgroundScanComplete();
+ mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
+
+ verify(mTunerCallbackMock, CALLBACK_TIMEOUT).onBackgroundScanComplete();
+ }
+
+ @Test
public void unregisterListCallback_withProgramUpdated_notInvokesCallback() throws Exception {
createRadioTuner();
mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
@@ -418,7 +501,7 @@
mProgramList.unregisterListCallback(mListCallbackMocks[0]);
mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
- verify(mListCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onItemChanged(any());
+ verify(mListCallbackMocks[0], after(TIMEOUT_MS).never()).onItemChanged(any());
}
@Test
@@ -457,7 +540,7 @@
mProgramList.removeOnCompleteListener(mOnCompleteListenerMocks[0]);
mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
- verify(mOnCompleteListenerMocks[0], CALLBACK_TIMEOUT.times(0)).onComplete();
+ verify(mOnCompleteListenerMocks[0], after(TIMEOUT_MS).never()).onComplete();
}
@Test
@@ -484,7 +567,10 @@
}
private void createRadioTuner() throws Exception {
- RadioManager radioManager = new RadioManager(mContext, mRadioServiceMock);
+ mApplicationInfo.targetSdkVersion = TEST_TARGET_SDK_VERSION;
+ when(mContextMock.getApplicationInfo()).thenReturn(mApplicationInfo);
+ RadioManager radioManager = new RadioManager(mContextMock, mRadioServiceMock);
+
RadioManager.BandConfig band = new RadioManager.FmBandConfig(
new RadioManager.FmBandDescriptor(RadioManager.REGION_ITU_1, RadioManager.BAND_FM,
/* lowerLimit= */ 87500, /* upperLimit= */ 108000, /* spacing= */ 200,
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
index 79a6b0d..ce3e019 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
@@ -777,6 +777,12 @@
}
@Test
+ public void toString_forModuleProperties() {
+ assertWithMessage("Module properties string").that(AMFM_PROPERTIES.toString())
+ .contains(AM_BAND_DESCRIPTOR.toString() + ", " + FM_BAND_DESCRIPTOR.toString());
+ }
+
+ @Test
public void writeToParcel_forModulePropertiesWithNullDabFrequencyTable() {
Parcel parcel = Parcel.obtain();
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
index 487086c..8b257e8 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.timeout;
@@ -51,7 +52,7 @@
private static final int TEST_TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
- private static final int CALLBACK_TIMEOUT_MS = 30_000;
+ private static final int CALLBACK_TIMEOUT_MS = 500;
private static final int AM_LOWER_LIMIT_KHZ = 150;
private static final RadioManager.BandConfig TEST_BAND_CONFIG = createBandConfig();
@@ -445,7 +446,17 @@
}
@Test
- public void getProgramInfo_beforeProgramInfoSetForTunerAdapter() {
+ public void getProgramInfo_withInvalidInput_fails() {
+ RadioManager.ProgramInfo[] programInfoArray = new RadioManager.ProgramInfo[2];
+
+ int status = mRadioTuner.getProgramInformation(programInfoArray);
+
+ assertWithMessage("Status for getting program info with input array of wrong size")
+ .that(status).isEqualTo(RadioManager.STATUS_BAD_VALUE);
+ }
+
+ @Test
+ public void getProgramInfo_beforeProgramInfoSetForTunerAdapter_fails() {
RadioManager.ProgramInfo[] programInfoArray = new RadioManager.ProgramInfo[1];
int status = mRadioTuner.getProgramInformation(programInfoArray);
@@ -732,6 +743,13 @@
}
@Test
+ public void onCurrentProgramInfoChanged_withNullInfo_notInvokeMockCallback() throws Exception {
+ mTunerCallback.onCurrentProgramInfoChanged(null);
+
+ verify(mCallbackMock, after(CALLBACK_TIMEOUT_MS).never()).onProgramInfoChanged(any());
+ }
+
+ @Test
public void onProgramListChanged_forTunerCallbackAdapter() throws Exception {
mTunerCallback.onProgramListChanged();
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index 01907fb..dea1b0e 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -16,8 +16,11 @@
package android.content.pm;
+import static org.junit.Assert.assertThrows;
+
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.ServiceSpecificException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
@@ -114,6 +117,34 @@
}
}
+ /**
+ * Test that exceptions created when parcelling data in the service are really
+ * sent to the client and re-thrown.
+ */
+ public void testThrownException() throws Exception {
+ final List<ThrowingObject> throwers = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ throwers.add(new ThrowingObject(/* throws= */ false));
+ }
+ throwers.add(new ThrowingObject(/* throws= */ true));
+
+ final ParceledListSlice<ThrowingObject> src = new ParceledListSlice<>(throwers);
+ src.setInlineCountLimit(1);
+
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(src, 0);
+ parcel.setDataPosition(0);
+
+ assertThrows(ServiceSpecificException.class, () -> {
+ final ParceledListSlice<ThrowingObject> dst =
+ parcel.readParcelable(getClass().getClassLoader());
+ });
+ } finally {
+ parcel.recycle();
+ }
+ }
+
private void sendParcelStringList(List<String> list) {
StringParceledListSlice slice;
Parcel parcel = Parcel.obtain();
@@ -236,6 +267,40 @@
};
}
+ public static class ThrowingObject implements Parcelable {
+
+ private final boolean mShouldThrow;
+
+ public ThrowingObject(boolean shouldThrow) {
+ mShouldThrow = shouldThrow;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mShouldThrow) {
+ throw new ServiceSpecificException(1234);
+ }
+ dest.writeBoolean(mShouldThrow);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ThrowingObject> CREATOR = new Creator<ThrowingObject>() {
+ @Override
+ public ThrowingObject createFromParcel(Parcel source) {
+ return new ThrowingObject(source.readBoolean());
+ }
+
+ @Override
+ public ThrowingObject[] newArray(int size) {
+ return new ThrowingObject[size];
+ }
+ };
+ }
+
public static class SmallObject extends BaseObject {
public int mFieldA;
public int mFieldB;
diff --git a/core/tests/coretests/src/android/hardware/input/InputManagerTest.kt b/core/tests/coretests/src/android/hardware/input/InputManagerTest.kt
new file mode 100644
index 0000000..ee7a608
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/InputManagerTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.input
+
+import android.content.res.Resources
+import android.platform.test.annotations.Presubmit
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.InputDevice
+import org.junit.After
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for [InputManager].
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:InputManagerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class InputManagerTest {
+
+ companion object {
+ const val DEVICE_ID = 42
+ const val SECOND_DEVICE_ID = 96
+ const val THIRD_DEVICE_ID = 99
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ private lateinit var inputManager: InputManager
+
+ private lateinit var devicesChangedListener: IInputDevicesChangedListener
+ private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
+
+ @Mock
+ private lateinit var iInputManager: IInputManager
+
+ @Before
+ fun setUp() {
+ inputManager = InputManager.resetInstance(iInputManager)
+ `when`(iInputManager.inputDeviceIds).then {
+ deviceGenerationMap.keys.toIntArray()
+ }
+ }
+
+ @After
+ fun tearDown() {
+ InputManager.clearInstance()
+ }
+
+ private fun notifyDeviceChanged(
+ deviceId: Int,
+ associatedDisplayId: Int,
+ usiVersion: HostUsiVersion?,
+ ) {
+ val generation = deviceGenerationMap[deviceId]?.plus(1)
+ ?: throw IllegalArgumentException("Device $deviceId was never added!")
+ deviceGenerationMap[deviceId] = generation
+
+ `when`(iInputManager.getInputDevice(deviceId))
+ .thenReturn(createInputDevice(deviceId, associatedDisplayId, usiVersion, generation))
+ val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
+ if (::devicesChangedListener.isInitialized) {
+ devicesChangedListener.onInputDevicesChanged(list.toIntArray())
+ }
+ }
+
+ private fun addInputDevice(
+ deviceId: Int,
+ associatedDisplayId: Int,
+ usiVersion: HostUsiVersion?,
+ ) {
+ deviceGenerationMap[deviceId] = 0
+ notifyDeviceChanged(deviceId, associatedDisplayId, usiVersion)
+ }
+
+ @Test
+ fun testUsiVersionDisplayAssociation() {
+ addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null)
+ addInputDevice(SECOND_DEVICE_ID, Display.INVALID_DISPLAY, HostUsiVersion(9, 8))
+ addInputDevice(THIRD_DEVICE_ID, 42, HostUsiVersion(3, 1))
+
+ val usiVersion = inputManager.getHostUsiVersion(createDisplay(42))
+ assertNotNull(usiVersion)
+ assertEquals(3, usiVersion!!.majorVersion)
+ assertEquals(1, usiVersion.minorVersion)
+ }
+
+ @Test
+ fun testUsiVersionFallBackToDisplayConfig() {
+ addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null)
+
+ `when`(iInputManager.getHostUsiVersionFromDisplayConfig(eq(42)))
+ .thenReturn(HostUsiVersion(9, 8))
+ val usiVersion = inputManager.getHostUsiVersion(createDisplay(42))
+ assertEquals(HostUsiVersion(9, 8), usiVersion)
+ }
+}
+
+private fun createInputDevice(
+ deviceId: Int,
+ associatedDisplayId: Int,
+ usiVersion: HostUsiVersion? = null,
+ generation: Int = -1,
+): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setAssociatedDisplayId(associatedDisplayId)
+ .setUsiVersion(usiVersion)
+ .setGeneration(generation)
+ .build()
+
+private fun createDisplay(displayId: Int): Display {
+ val res: Resources? = null
+ return Display(null /* global */, displayId, DisplayInfo(), res)
+}
diff --git a/core/tests/coretests/src/android/util/TypedValueTest.kt b/core/tests/coretests/src/android/util/TypedValueTest.kt
index af01447..d4e77b5 100644
--- a/core/tests/coretests/src/android/util/TypedValueTest.kt
+++ b/core/tests/coretests/src/android/util/TypedValueTest.kt
@@ -276,5 +276,13 @@
assertThat(dimenValueToTest)
.isWithin(0.05f)
.of(actualDimenValue)
+
+ // Also test the alias functions
+ assertThat(TypedValue.convertDimensionToPixels(dimenType, dimenValueToTest, metrics))
+ .isWithin(0.05f)
+ .of(actualPx)
+ assertThat(TypedValue.convertPixelsToDimension(dimenType, actualPx, metrics))
+ .isWithin(0.05f)
+ .of(actualDimenValue)
}
}
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
index 64df348..e82b699 100755
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
@@ -41,34 +41,58 @@
new EqualsTester()
.addEqualityGroup(
- new HdmiPortInfo(portId, portType, address, isCec, isMhl, isArcSupported,
- isEarcSupported),
- new HdmiPortInfo(portId, portType, address, isCec, isMhl, isArcSupported,
- isEarcSupported))
+ new HdmiPortInfo.Builder(portId, portType, address)
+ .setCecSupported(isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(isEarcSupported),
+ new HdmiPortInfo.Builder(portId, portType, address)
+ .setCecSupported(isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(isEarcSupported))
.addEqualityGroup(
- new HdmiPortInfo(
- portId + 1, portType, address, isCec, isMhl, isArcSupported,
- isEarcSupported))
+ new HdmiPortInfo.Builder(portId + 1, portType, address)
+ .setCecSupported(isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(isEarcSupported))
.addEqualityGroup(
- new HdmiPortInfo(
- portId, portType + 1, address, isCec, isMhl, isArcSupported,
- isEarcSupported))
+ new HdmiPortInfo.Builder(portId, portType + 1, address)
+ .setCecSupported(isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(isEarcSupported))
.addEqualityGroup(
- new HdmiPortInfo(
- portId, portType, address + 1, isCec, isMhl, isArcSupported,
- isEarcSupported))
+ new HdmiPortInfo.Builder(portId, portType, address + 1)
+ .setCecSupported(isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(isEarcSupported))
.addEqualityGroup(
- new HdmiPortInfo(portId, portType, address, !isCec, isMhl, isArcSupported,
- isEarcSupported))
+ new HdmiPortInfo.Builder(portId, portType, address)
+ .setCecSupported(!isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(isEarcSupported))
.addEqualityGroup(
- new HdmiPortInfo(portId, portType, address, isCec, !isMhl, isArcSupported,
- isEarcSupported))
+ new HdmiPortInfo.Builder(portId, portType, address)
+ .setCecSupported(isCec)
+ .setMhlSupported(!isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(isEarcSupported))
.addEqualityGroup(
- new HdmiPortInfo(portId, portType, address, isCec, isMhl, !isArcSupported,
- isEarcSupported))
+ new HdmiPortInfo.Builder(portId, portType, address)
+ .setCecSupported(isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(!isArcSupported)
+ .setEarcSupported(isEarcSupported))
.addEqualityGroup(
- new HdmiPortInfo(portId, portType, address, isCec, isMhl, isArcSupported,
- !isEarcSupported))
+ new HdmiPortInfo.Builder(portId, portType, address)
+ .setCecSupported(isCec)
+ .setMhlSupported(isMhl)
+ .setArcSupported(isArcSupported)
+ .setEarcSupported(!isEarcSupported))
.testEquals();
}
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index bf7f3bd..a37cb80 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -310,6 +310,7 @@
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<!-- Permission required for UiModeManager CTS test -->
<permission name="android.permission.READ_PROJECTION_STATE"/>
+ <permission name="android.permission.READ_WALLPAPER_INTERNAL"/>
<permission name="android.permission.READ_WIFI_CREDENTIAL"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 8b7265e..bccf283 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -3391,6 +3391,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "1015746067": {
+ "message": "Display id=%d is ignoring orientation request for %d, return %d following a per-app override for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"1022095595": {
"message": "TaskFragment info changed name=%s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 3c654d6..046373d 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -26,7 +26,9 @@
import android.hardware.HardwareBuffer;
import android.os.Build;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.SharedMemory;
import android.os.StrictMode;
import android.os.Trace;
import android.util.DisplayMetrics;
@@ -38,6 +40,7 @@
import libcore.util.NativeAllocationRegistry;
+import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.nio.Buffer;
@@ -90,6 +93,7 @@
private boolean mRecycled;
private ColorSpace mColorSpace;
+ private Gainmap mGainmap;
/*package*/ int mDensity = getDefaultDensity();
@@ -738,6 +742,26 @@
}
/**
+ * Returns the shared memory handle to the pixel storage if the bitmap is already using
+ * shared memory and null if it is not. The SharedMemory object is then useful to then pass
+ * through HIDL APIs (e.g. WearOS's DisplayOffload service).
+ *
+ * @hide
+ */
+ public SharedMemory getSharedMemory() {
+ checkRecycled("Cannot access shared memory of a recycled bitmap");
+ if (nativeIsBackedByAshmem(mNativePtr)) {
+ try {
+ int fd = nativeGetAshmemFD(mNativePtr);
+ return SharedMemory.fromFileDescriptor(ParcelFileDescriptor.fromFd(fd));
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to create dup'd file descriptor for shared bitmap memory");
+ }
+ }
+ return null;
+ }
+
+ /**
* Create a hardware bitmap backed by a {@link HardwareBuffer}.
*
* <p>The passed HardwareBuffer's usage flags must contain
@@ -1874,6 +1898,27 @@
}
/**
+ * Returns whether or not this Bitmap contains a Gainmap.
+ * @hide
+ */
+ public boolean hasGainmap() {
+ checkRecycled("Bitmap is recycled");
+ return nativeHasGainmap(mNativePtr);
+ }
+
+ /**
+ * Returns the gainmap or null if the bitmap doesn't contain a gainmap
+ * @hide
+ */
+ public @Nullable Gainmap getGainmap() {
+ checkRecycled("Bitmap is recycled");
+ if (mGainmap == null) {
+ mGainmap = nativeExtractGainmap(mNativePtr);
+ }
+ return mGainmap;
+ }
+
+ /**
* Fills the bitmap's pixels with the specified {@link Color}.
*
* @throws IllegalStateException if the bitmap is not mutable.
@@ -2294,6 +2339,7 @@
boolean isMutable);
private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
private static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig);
+ private static native int nativeGetAshmemFD(long nativeBitmap);
private static native long nativeGetNativeFinalizer();
private static native void nativeRecycle(long nativeBitmap);
@UnsupportedAppUsage
@@ -2356,6 +2402,8 @@
private static native void nativeSetImmutable(long nativePtr);
+ private static native Gainmap nativeExtractGainmap(long nativePtr);
+
// ---------------- @CriticalNative -------------------
@CriticalNative
@@ -2363,4 +2411,7 @@
@CriticalNative
private static native boolean nativeIsBackedByAshmem(long nativePtr);
+
+ @CriticalNative
+ private static native boolean nativeHasGainmap(long nativePtr);
}
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
new file mode 100644
index 0000000..a25a605
--- /dev/null
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.NonNull;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Gainmap represents a mechanism for augmenting an SDR image to produce an HDR one with variable
+ * display adjustment capability.
+ *
+ * It is a combination of a set of metadata describing the gainmap, as well as either a 1 or 3
+ * channel Bitmap that represents the gainmap data itself.
+ *
+ * @hide
+ */
+public class Gainmap {
+ private final long mNativePtr;
+ private final Bitmap mGainmapImage;
+
+ // called from JNI and Bitmap_Delegate.
+ private Gainmap(Bitmap gainmapImage, long nativeGainmap, int allocationByteCount,
+ boolean fromMalloc) {
+ if (nativeGainmap == 0) {
+ throw new RuntimeException("internal error: native gainmap is 0");
+ }
+
+ mGainmapImage = gainmapImage;
+ mNativePtr = nativeGainmap;
+
+ final NativeAllocationRegistry registry;
+ if (fromMalloc) {
+ registry = NativeAllocationRegistry.createMalloced(
+ Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount);
+ } else {
+ registry = NativeAllocationRegistry.createNonmalloced(
+ Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount);
+ }
+ registry.registerNativeAllocation(this, nativeGainmap);
+ }
+
+ /**
+ * Returns the image data of the gainmap represented as a Bitmap
+ * @return
+ */
+ @NonNull
+ public Bitmap getGainmapImage() {
+ return mGainmapImage;
+ }
+
+ /**
+ * Sets the gainmap max metadata. For single-plane gainmaps, r, g, and b should be the same.
+ */
+ @NonNull
+ public void setGainmapMax(float r, float g, float b) {
+ nSetGainmapMax(mNativePtr, r, g, b);
+ }
+
+ /**
+ * Gets the gainmap max metadata. For single-plane gainmaps, all 3 components should be the
+ * same. The components are in r, g, b order.
+ */
+ @NonNull
+ public float[] getGainmapMax() {
+ float[] ret = new float[3];
+ nGetGainmapMax(mNativePtr, ret);
+ return ret;
+ }
+
+ /**
+ * Sets the maximum HDR ratio for the gainmap
+ */
+ @NonNull
+ public void setHdrRatioMax(float max) {
+ nSetHdrRatioMax(mNativePtr, max);
+ }
+
+ /**
+ * Gets the maximum HDR ratio for the gainmap
+ */
+ @NonNull
+ public float getHdrRatioMax() {
+ return nGetHdrRatioMax(mNativePtr);
+ }
+
+ /**
+ * Sets the maximum HDR ratio for the gainmap
+ */
+ @NonNull
+ public void setHdrRatioMin(float min) {
+ nSetHdrRatioMin(mNativePtr, min);
+ }
+
+ /**
+ * Gets the maximum HDR ratio for the gainmap
+ */
+ @NonNull
+ public float getHdrRatioMin() {
+ return nGetHdrRatioMin(mNativePtr);
+ }
+
+ private static native long nGetFinalizer();
+
+ private static native void nSetGainmapMax(long ptr, float r, float g, float b);
+ private static native void nGetGainmapMax(long ptr, float[] components);
+
+ private static native void nSetHdrRatioMax(long ptr, float max);
+ private static native float nGetHdrRatioMax(long ptr);
+
+ private static native void nSetHdrRatioMin(long ptr, float min);
+ private static native float nGetHdrRatioMin(long ptr);
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index ee8ec1d..67963a3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -109,38 +109,38 @@
* be resized based on {@param launchingFragmentBounds}.
* Otherwise, we will create a new TaskFragment with the given
* token for the {@param launchingActivity}.
- * @param launchingFragmentBounds the initial bounds for the launching TaskFragment.
+ * @param launchingRelBounds the initial relative bounds for the launching TaskFragment.
* @param launchingActivity the Activity to put on the left hand side of the split as the
* primary.
* @param secondaryFragmentToken token to create the secondary TaskFragment with.
- * @param secondaryFragmentBounds the initial bounds for the secondary TaskFragment
+ * @param secondaryRelBounds the initial relative bounds for the secondary TaskFragment
* @param activityIntent Intent to start the secondary Activity with.
* @param activityOptions ActivityOptions to start the secondary Activity with.
* @param windowingMode the windowing mode to set for the TaskFragments.
* @param splitAttributes the {@link SplitAttributes} to represent the split.
*/
void startActivityToSide(@NonNull WindowContainerTransaction wct,
- @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds,
+ @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingRelBounds,
@NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken,
- @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent,
+ @NonNull Rect secondaryRelBounds, @NonNull Intent activityIntent,
@Nullable Bundle activityOptions, @NonNull SplitRule rule,
@WindowingMode int windowingMode, @NonNull SplitAttributes splitAttributes) {
final IBinder ownerToken = launchingActivity.getActivityToken();
// Create or resize the launching TaskFragment.
if (mFragmentInfos.containsKey(launchingFragmentToken)) {
- resizeTaskFragment(wct, launchingFragmentToken, launchingFragmentBounds);
+ resizeTaskFragment(wct, launchingFragmentToken, launchingRelBounds);
updateWindowingMode(wct, launchingFragmentToken, windowingMode);
} else {
createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken,
- launchingFragmentBounds, windowingMode, launchingActivity);
+ launchingRelBounds, windowingMode, launchingActivity);
}
updateAnimationParams(wct, launchingFragmentToken, splitAttributes);
// Create a TaskFragment for the secondary activity.
final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder(
getOrganizerToken(), secondaryFragmentToken, ownerToken)
- .setInitialBounds(secondaryFragmentBounds)
+ .setInitialRelativeBounds(secondaryRelBounds)
.setWindowingMode(windowingMode)
// Make sure to set the paired fragment token so that the new TaskFragment will be
// positioned right above the paired TaskFragment.
@@ -190,8 +190,9 @@
* have to be a child of this task fragment, but must belong to the same task.
*/
void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
- @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) {
- createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode,
+ @NonNull IBinder ownerToken, @NonNull Rect relBounds,
+ @WindowingMode int windowingMode) {
+ createTaskFragment(wct, fragmentToken, ownerToken, relBounds, windowingMode,
null /* pairedActivityToken */);
}
@@ -203,11 +204,11 @@
* positioned right above it.
*/
void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
- @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode,
+ @NonNull IBinder ownerToken, @NonNull Rect relBounds, @WindowingMode int windowingMode,
@Nullable IBinder pairedActivityToken) {
final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder(
getOrganizerToken(), fragmentToken, ownerToken)
- .setInitialBounds(bounds)
+ .setInitialRelativeBounds(relBounds)
.setWindowingMode(windowingMode)
.setPairedActivityToken(pairedActivityToken)
.build();
@@ -229,10 +230,10 @@
* have to be a child of this task fragment, but must belong to the same task.
*/
private void createTaskFragmentAndReparentActivity(@NonNull WindowContainerTransaction wct,
- @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect bounds,
+ @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect relBounds,
@WindowingMode int windowingMode, @NonNull Activity activity) {
final IBinder reparentActivityToken = activity.getActivityToken();
- createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode,
+ createTaskFragment(wct, fragmentToken, ownerToken, relBounds, windowingMode,
reparentActivityToken);
wct.reparentActivityToTaskFragment(fragmentToken, reparentActivityToken);
}
@@ -280,15 +281,15 @@
}
void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
- @Nullable Rect bounds) {
+ @Nullable Rect relBounds) {
if (!mFragmentInfos.containsKey(fragmentToken)) {
throw new IllegalArgumentException(
"Can't find an existing TaskFragment with fragmentToken=" + fragmentToken);
}
- if (bounds == null) {
- bounds = new Rect();
+ if (relBounds == null) {
+ relBounds = new Rect();
}
- wct.setBounds(mFragmentInfos.get(fragmentToken).getToken(), bounds);
+ wct.setRelativeBounds(mFragmentInfos.get(fragmentToken).getToken(), relBounds);
}
void updateWindowingMode(@NonNull WindowContainerTransaction wct,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 85a00df..2b93682 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -180,22 +180,21 @@
primaryActivity, secondaryIntent);
final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
minDimensionsPair);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
- primaryActivity, primaryRectBounds, splitAttributes, null /* containerToAvoid */);
+ primaryActivity, primaryRelBounds, splitAttributes, null /* containerToAvoid */);
// Create new empty task fragment
final int taskId = primaryContainer.getTaskId();
final TaskFragmentContainer secondaryContainer = mController.newContainer(
secondaryIntent, primaryActivity, taskId);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(secondaryRectBounds);
+ .getWindowingModeForSplitTaskFragment(secondaryRelBounds);
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
- primaryActivity.getActivityToken(), secondaryRectBounds,
- windowingMode);
+ primaryActivity.getActivityToken(), secondaryRelBounds, windowingMode);
updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
// Set adjacent to each other so that the containers below will be invisible.
@@ -227,12 +226,12 @@
secondaryActivity);
final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
minDimensionsPair);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
- primaryActivity, primaryRectBounds, splitAttributes, null /* containerToAvoid */);
+ primaryActivity, primaryRelBounds, splitAttributes, null /* containerToAvoid */);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
final TaskFragmentContainer curSecondaryContainer = mController.getContainerWithActivity(
secondaryActivity);
@@ -244,7 +243,7 @@
containerToAvoid = curSecondaryContainer;
}
final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
- secondaryActivity, secondaryRectBounds, splitAttributes, containerToAvoid);
+ secondaryActivity, secondaryRelBounds, splitAttributes, containerToAvoid);
// Set adjacent to each other so that the containers below will be invisible.
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule,
@@ -261,23 +260,23 @@
*/
private TaskFragmentContainer prepareContainerForActivity(
@NonNull WindowContainerTransaction wct, @NonNull Activity activity,
- @NonNull Rect bounds, @NonNull SplitAttributes splitAttributes,
+ @NonNull Rect relBounds, @NonNull SplitAttributes splitAttributes,
@Nullable TaskFragmentContainer containerToAvoid) {
TaskFragmentContainer container = mController.getContainerWithActivity(activity);
final int taskId = container != null ? container.getTaskId() : activity.getTaskId();
if (container == null || container == containerToAvoid) {
container = mController.newContainer(activity, taskId);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(bounds);
+ .getWindowingModeForSplitTaskFragment(relBounds);
final IBinder reparentActivityToken = activity.getActivityToken();
createTaskFragment(wct, container.getTaskFragmentToken(), reparentActivityToken,
- bounds, windowingMode, reparentActivityToken);
+ relBounds, windowingMode, reparentActivityToken);
wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(),
reparentActivityToken);
} else {
- resizeTaskFragmentIfRegistered(wct, container, bounds);
+ resizeTaskFragmentIfRegistered(wct, container, relBounds);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(bounds);
+ .getWindowingModeForSplitTaskFragment(relBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
}
updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes);
@@ -301,9 +300,9 @@
@Nullable Bundle activityOptions, @NonNull SplitRule rule,
@NonNull SplitAttributes splitAttributes, boolean isPlaceholder) {
final TaskProperties taskProperties = getTaskProperties(launchingActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
@@ -320,11 +319,11 @@
primaryContainer);
final TaskContainer taskContainer = mController.getTaskContainer(taskId);
final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
- primaryRectBounds);
+ primaryRelBounds);
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule, splitAttributes);
- startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
- launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
+ startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRelBounds,
+ launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRelBounds,
activityIntent, activityOptions, rule, windowingMode, splitAttributes);
if (isPlaceholder) {
// When placeholder is launched in split, we should keep the focus on the primary.
@@ -351,20 +350,20 @@
}
final TaskProperties taskProperties = getTaskProperties(updatedContainer);
final SplitAttributes splitAttributes = splitContainer.getSplitAttributes();
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
// Whether the placeholder is becoming side-by-side with the primary from fullscreen.
final boolean isPlaceholderBecomingSplit = splitContainer.isPlaceholderContainer()
&& secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */)
- && !secondaryRectBounds.isEmpty();
+ && !secondaryRelBounds.isEmpty();
// If the task fragments are not registered yet, the positions will be updated after they
// are created again.
- resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
- resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
+ resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRelBounds);
+ resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRelBounds);
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule,
splitAttributes);
if (isPlaceholderBecomingSplit) {
@@ -373,7 +372,7 @@
}
final TaskContainer taskContainer = updatedContainer.getTaskContainer();
final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
- primaryRectBounds);
+ primaryRelBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
updateAnimationParams(wct, primaryContainer.getTaskFragmentToken(), splitAttributes);
@@ -405,11 +404,11 @@
// TODO(b/190433398): Handle resize if the fragment hasn't appeared yet.
private void resizeTaskFragmentIfRegistered(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
- @Nullable Rect bounds) {
+ @Nullable Rect relBounds) {
if (container.getInfo() == null) {
return;
}
- resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
+ resizeTaskFragment(wct, container.getTaskFragmentToken(), relBounds);
}
private void updateTaskFragmentWindowingModeIfRegistered(
@@ -431,27 +430,27 @@
"Creating a task fragment that is not registered with controller.");
}
- container.setLastRequestedBounds(fragmentOptions.getInitialBounds());
+ container.setLastRequestedBounds(fragmentOptions.getInitialRelativeBounds());
container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode());
super.createTaskFragment(wct, fragmentOptions);
}
@Override
void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
- @Nullable Rect bounds) {
+ @Nullable Rect relBounds) {
TaskFragmentContainer container = mController.getContainer(fragmentToken);
if (container == null) {
throw new IllegalStateException(
"Resizing a task fragment that is not registered with controller.");
}
- if (container.areLastRequestedBoundsEqual(bounds)) {
+ if (container.areLastRequestedBoundsEqual(relBounds)) {
// Return early if the provided bounds were already requested
return;
}
- container.setLastRequestedBounds(bounds);
- super.resizeTaskFragment(wct, fragmentToken, bounds);
+ container.setLastRequestedBounds(relBounds);
+ super.resizeTaskFragment(wct, fragmentToken, relBounds);
}
@Override
@@ -658,7 +657,7 @@
@VisibleForTesting
@NonNull
- Rect getBoundsForPosition(@Position int position, @NonNull TaskProperties taskProperties,
+ Rect getRelBoundsForPosition(@Position int position, @NonNull TaskProperties taskProperties,
@NonNull SplitAttributes splitAttributes) {
final Configuration taskConfiguration = taskProperties.getConfiguration();
final FoldingFeature foldingFeature = getFoldingFeature(taskProperties);
@@ -671,16 +670,24 @@
if (!shouldShowSplit(computedSplitAttributes)) {
return new Rect();
}
+ final Rect bounds;
switch (position) {
case POSITION_START:
- return getPrimaryBounds(taskConfiguration, computedSplitAttributes, foldingFeature);
- case POSITION_END:
- return getSecondaryBounds(taskConfiguration, computedSplitAttributes,
+ bounds = getPrimaryBounds(taskConfiguration, computedSplitAttributes,
foldingFeature);
+ break;
+ case POSITION_END:
+ bounds = getSecondaryBounds(taskConfiguration, computedSplitAttributes,
+ foldingFeature);
+ break;
case POSITION_FILL:
default:
- return new Rect();
+ bounds = new Rect();
}
+ // Convert to relative bounds in parent coordinate. This is to avoid flicker when the Task
+ // resized before organizer requests have been applied.
+ taskProperties.translateAbsoluteBoundsToRelativeBounds(bounds);
+ return bounds;
}
@NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 03f4dc9..f41295b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -243,6 +243,15 @@
return mConfiguration;
}
+ /** Translates the given absolute bounds to relative bounds in this Task coordinate. */
+ void translateAbsoluteBoundsToRelativeBounds(@NonNull Rect inOutBounds) {
+ if (inOutBounds.isEmpty()) {
+ return;
+ }
+ final Rect taskBounds = mConfiguration.windowConfiguration.getBounds();
+ inOutBounds.offset(-taskBounds.left, -taskBounds.top);
+ }
+
/**
* Obtains the {@link TaskProperties} for the task that the provided {@link Activity} is
* associated with.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 17814c6..861cb49 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -570,20 +570,22 @@
/**
* Checks if last requested bounds are equal to the provided value.
+ * The requested bounds are relative bounds in parent coordinate.
*/
- boolean areLastRequestedBoundsEqual(@Nullable Rect bounds) {
- return (bounds == null && mLastRequestedBounds.isEmpty())
- || mLastRequestedBounds.equals(bounds);
+ boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) {
+ return (relBounds == null && mLastRequestedBounds.isEmpty())
+ || mLastRequestedBounds.equals(relBounds);
}
/**
* Updates the last requested bounds.
+ * The requested bounds are relative bounds in parent coordinate.
*/
- void setLastRequestedBounds(@Nullable Rect bounds) {
- if (bounds == null) {
+ void setLastRequestedBounds(@Nullable Rect relBounds) {
+ if (relBounds == null) {
mLastRequestedBounds.setEmpty();
} else {
- mLastRequestedBounds.set(bounds);
+ mLastRequestedBounds.set(relBounds);
}
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index c5d932e..a41e63f 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -75,6 +75,7 @@
import android.window.TaskFragmentOperation;
import android.window.WindowContainerTransaction;
+import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -148,13 +149,13 @@
mPresenter.resizeTaskFragment(mTransaction, container.getTaskFragmentToken(), TASK_BOUNDS);
assertTrue(container.areLastRequestedBoundsEqual(TASK_BOUNDS));
- verify(mTransaction).setBounds(any(), eq(TASK_BOUNDS));
+ verify(mTransaction).setRelativeBounds(any(), eq(TASK_BOUNDS));
// No request to set the same bounds.
clearInvocations(mTransaction);
mPresenter.resizeTaskFragment(mTransaction, container.getTaskFragmentToken(), TASK_BOUNDS);
- verify(mTransaction, never()).setBounds(any(), any());
+ verify(mTransaction, never()).setRelativeBounds(any(), any());
}
@Test
@@ -226,7 +227,7 @@
}
@Test
- public void testGetBoundsForPosition_expandContainers() {
+ public void testGetRelBoundsForPosition_expandContainers() {
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType())
@@ -234,18 +235,40 @@
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
- public void testGetBoundsForPosition_splitVertically() {
+ public void testGetRelBoundsForPosition_expandContainers_isRelativeToParent() {
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty(
+ new Rect(100, 100, 500, 1000));
+ final SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType())
+ .build();
+
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
+
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ }
+
+ @Test
+ public void testGetRelBoundsForPosition_splitVertically() {
final Rect primaryBounds = getSplitBounds(true /* isPrimary */,
false /* splitHorizontally */);
final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
@@ -258,14 +281,15 @@
assertEquals("Primary bounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("Secondary bounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
splitAttributes = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
@@ -274,14 +298,15 @@
assertEquals("Secondary bounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("Primary bounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
splitAttributes = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
@@ -292,18 +317,85 @@
assertEquals("Secondary bounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("Primary bounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
- public void testGetBoundsForPosition_splitHorizontally() {
+ public void testGetRelBoundsForPosition_splitVertically_isRelativeToParent() {
+ // Calculate based on TASK_BOUNDS.
+ final Rect primaryBounds = getSplitBounds(true /* isPrimary */,
+ false /* splitHorizontally */);
+ final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
+ false /* splitHorizontally */);
+
+ // Offset TaskBounds to 100, 100. The returned rel bounds shouldn't be affected.
+ final Rect taskBounds = new Rect(TASK_BOUNDS);
+ taskBounds.offset(100, 100);
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty(taskBounds);
+ SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+ .build();
+
+ assertEquals("Primary bounds must be reported.",
+ primaryBounds,
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
+
+ assertEquals("Secondary bounds must be reported.",
+ secondaryBounds,
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+
+ splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.RIGHT_TO_LEFT)
+ .build();
+
+ assertEquals("Secondary bounds must be reported.",
+ secondaryBounds,
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
+
+ assertEquals("Primary bounds must be reported.",
+ primaryBounds,
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+
+ splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+ .build();
+ // Layout direction should follow screen layout for SplitAttributes.LayoutDirection.LOCALE.
+ taskProperties.getConfiguration().screenLayout |= Configuration.SCREENLAYOUT_LAYOUTDIR_RTL;
+
+ assertEquals("Secondary bounds must be reported.",
+ secondaryBounds,
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
+
+ assertEquals("Primary bounds must be reported.",
+ primaryBounds,
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ }
+
+ @Test
+ public void testGetRelBoundsForPosition_splitHorizontally() {
final Rect primaryBounds = getSplitBounds(true /* isPrimary */,
true /* splitHorizontally */);
final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
@@ -316,14 +408,15 @@
assertEquals("Primary bounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("Secondary bounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
splitAttributes = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
@@ -332,18 +425,19 @@
assertEquals("Secondary bounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("Primary bounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
- public void testGetBoundsForPosition_useHingeFallback() {
+ public void testGetRelBoundsForPosition_useHingeFallback() {
final Rect primaryBounds = getSplitBounds(true /* isPrimary */,
false /* splitHorizontally */);
final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
@@ -361,14 +455,15 @@
assertEquals("PrimaryBounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("SecondaryBounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
// Hinge is reported, but the host task is in multi-window mode. Still use fallback
// splitType.
@@ -379,14 +474,15 @@
assertEquals("PrimaryBounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("SecondaryBounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
// Hinge is reported, and the host task is in fullscreen, but layout direction doesn't match
// folding area orientation. Still use fallback splitType.
@@ -397,18 +493,19 @@
assertEquals("PrimaryBounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("SecondaryBounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
- public void testGetBoundsForPosition_fallbackToExpandContainers() {
+ public void testGetRelBoundsForPosition_fallbackToExpandContainers() {
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.HingeSplitType(
@@ -418,18 +515,19 @@
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
- public void testGetBoundsForPosition_useHingeSplitType() {
+ public void testGetRelBoundsForPosition_useHingeSplitType() {
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.HingeSplitType(
@@ -455,14 +553,15 @@
assertEquals("PrimaryBounds must be reported.",
primaryBounds,
- mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes));
assertEquals("SecondaryBounds must be reported.",
secondaryBounds,
- mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
assertEquals("Task bounds must be reported.",
new Rect(),
- mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
@@ -601,8 +700,12 @@
}
private static TaskContainer.TaskProperties getTaskProperty() {
+ return getTaskProperty(TASK_BOUNDS);
+ }
+
+ private static TaskContainer.TaskProperties getTaskProperty(@NonNull Rect taskBounds) {
final Configuration configuration = new Configuration();
- configuration.windowConfiguration.setBounds(TASK_BOUNDS);
+ configuration.windowConfiguration.setBounds(taskBounds);
return new TaskContainer.TaskProperties(DEFAULT_DISPLAY, configuration);
}
}
diff --git a/libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml b/libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml
new file mode 100644
index 0000000..a3ca74f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_900" android:alpha="0.6" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml
new file mode 100644
index 0000000..a3ca74f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_900" android:alpha="0.6" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
index 416287d..53a8bb1 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
@@ -17,5 +17,4 @@
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
- <corners android:radius="20dp" />
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml
new file mode 100644
index 0000000..60f3cfe
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorAccentPrimaryVariant"/>
+ <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml
new file mode 100644
index 0000000..ef97ea1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/letterbox_restart_button_background_ripple">
+ <item android:drawable="@drawable/letterbox_restart_button_background"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml
new file mode 100644
index 0000000..c247c6e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_checked="true"
+ android:drawable="@drawable/letterbox_restart_checkbox_checked" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/letterbox_restart_checkbox_checked" />
+ <item android:state_pressed="false"
+ android:drawable="@drawable/letterbox_restart_checkbox_unchecked" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml
new file mode 100644
index 0000000..4f97e2c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/textColorSecondary">
+ <group
+ android:scaleX="0.83333333333"
+ android:scaleY="0.83333333333"
+ android:translateX="0"
+ android:translateY="0">
+ <path
+ android:fillColor="?android:attr/textColorSecondary"
+ android:pathData="M10.6,16.2 L17.65,9.15 16.25,7.75 10.6,13.4 7.75,10.55 6.35,11.95ZM5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21Z"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml
new file mode 100644
index 0000000..bb14d19
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/textColorSecondary">
+ <group
+ android:scaleX="0.83333333333"
+ android:scaleY="0.83333333333"
+ android:translateX="0"
+ android:translateY="0">
+ <path
+ android:fillColor="?android:attr/textColorSecondary"
+ android:pathData="M5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21ZM5,19H19Q19,19 19,19Q19,19 19,19V5Q19,5 19,5Q19,5 19,5H5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19Z"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml
new file mode 100644
index 0000000..e3c18a2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface"/>
+ <corners android:radius="@dimen/letterbox_restart_dialog_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml
new file mode 100644
index 0000000..af89d41
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant" android:width="1dp"/>
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml
new file mode 100644
index 0000000..e32aefc
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/letterbox_restart_dismiss_button_background_ripple">
+ <item android:drawable="@drawable/letterbox_restart_dismiss_button_background"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml
new file mode 100644
index 0000000..5053971
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:width="@dimen/letterbox_restart_dialog_title_icon_width"
+ android:height="@dimen/letterbox_restart_dialog_title_icon_height"
+ android:viewportWidth="45"
+ android:viewportHeight="44">
+ <group
+ android:scaleX="0.8"
+ android:scaleY="0.8"
+ android:translateX="8"
+ android:translateY="8">
+ <path
+ android:pathData="M0,36V24.5H3V30.85L10.4,23.45L12.55,25.6L5.15,33H11.5V36H0ZM24.5,36V33H30.85L23.5,25.65L25.65,23.5L33,30.85V24.5H36V36H24.5ZM10.35,12.5L3,5.15V11.5H0V0H11.5V3H5.15L12.5,10.35L10.35,12.5ZM25.65,12.5L23.5,10.35L30.85,3H24.5V0H36V11.5H33V5.15L25.65,12.5Z"
+ android:fillColor="?androidprv:attr/colorAccentPrimaryVariant"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml
new file mode 100644
index 0000000..b6e0172
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/letterbox_restart_dialog_title_icon_width"
+ android:height="@dimen/letterbox_restart_dialog_title_icon_height"
+ android:viewportWidth="45"
+ android:viewportHeight="44">
+ <group
+ android:scaleX="0.8"
+ android:scaleY="0.8"
+ android:translateX="8"
+ android:translateY="8">
+ <path
+ android:pathData="M0,36V24.5H3V30.85L10.4,23.45L12.55,25.6L5.15,33H11.5V36H0ZM24.5,36V33H30.85L23.5,25.65L25.65,23.5L33,30.85V24.5H36V36H24.5ZM10.35,12.5L3,5.15V11.5H0V0H11.5V3H5.15L12.5,10.35L10.35,12.5ZM25.65,12.5L23.5,10.35L30.85,3H24.5V0H36V11.5H33V5.15L25.65,12.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
index 2a4cc02..da31a46 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
@@ -17,26 +17,14 @@
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/desktop_mode_caption"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
android:background="@drawable/desktop_mode_decor_title">
<Button
- style="@style/CaptionButtonStyle"
- android:id="@+id/back_button"
- android:contentDescription="@string/back_button_text"
- android:background="@drawable/decor_back_button_dark"
- />
- <Button
android:id="@+id/caption_handle"
android:layout_width="128dp"
android:layout_height="32dp"
- android:layout_margin="5dp"
- android:padding="4dp"
android:contentDescription="@string/handle_text"
android:background="@drawable/decor_handle_dark"/>
- <Button
- style="@style/CaptionButtonStyle"
- android:id="@+id/close_window"
- android:contentDescription="@string/close_button_text"
- android:background="@drawable/decor_close_button_dark"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
new file mode 100644
index 0000000..ba9852c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
@@ -0,0 +1,142 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.wm.shell.compatui.RestartDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/system_neutral1_900">
+
+ <!-- The background of the top-level layout acts as the background dim. -->
+
+ <!--TODO (b/266288912): Resolve overdraw warning -->
+
+ <!-- Vertical margin will be set dynamically since it depends on task bounds.
+ Setting the alpha of the dialog container to 0, since it shouldn't be visible until the
+ enter animation starts. -->
+ <FrameLayout
+ android:id="@+id/letterbox_restart_dialog_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/letterbox_restart_dialog_margin"
+ android:background="@drawable/letterbox_restart_dialog_background"
+ android:alpha="0"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintWidth_max="@dimen/letterbox_restart_dialog_width">
+
+ <!-- The ScrollView should only wrap the content of the dialog, otherwise the background
+ corner radius will be cut off when scrolling to the top/bottom. -->
+
+ <ScrollView android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:padding="24dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:importantForAccessibility="no"
+ android:layout_width="@dimen/letterbox_restart_dialog_title_icon_width"
+ android:layout_height="@dimen/letterbox_restart_dialog_title_icon_height"
+ android:src="@drawable/letterbox_restart_header_ic_arrows"/>
+
+ <TextView
+ android:layout_marginVertical="16dp"
+ android:id="@+id/letterbox_restart_dialog_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/letterbox_restart_dialog_title"
+ android:textAlignment="center"
+ android:textAppearance="@style/RestartDialogTitleText"/>
+
+ <TextView
+ android:textAppearance="@style/RestartDialogBodyText"
+ android:id="@+id/letterbox_restart_dialog_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/letterbox_restart_dialog_description"
+ android:textAlignment="center"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="center_vertical"
+ android:layout_marginVertical="32dp">
+
+ <CheckBox
+ android:id="@+id/letterbox_restart_dialog_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:button="@drawable/letterbox_restart_checkbox_button"/>
+
+ <TextView
+ android:textAppearance="@style/RestartDialogCheckboxText"
+ android:layout_marginStart="12dp"
+ android:id="@+id/letterbox_restart_dialog_checkbox_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/letterbox_restart_dialog_checkbox_title"
+ android:textAlignment="textStart"/>
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:minHeight="@dimen/letterbox_restart_dialog_button_height"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:textAppearance="@style/RestartDialogDismissButton"
+ android:id="@+id/letterbox_restart_dialog_dismiss_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="@dimen/letterbox_restart_dialog_button_width"
+ android:minHeight="@dimen/letterbox_restart_dialog_button_height"
+ android:layout_gravity="start"
+ android:background=
+ "@drawable/letterbox_restart_dismiss_button_background_ripple"
+ android:text="@string/letterbox_restart_cancel"
+ android:contentDescription="@string/letterbox_restart_cancel"/>
+
+ <Button
+ android:textAppearance="@style/RestartDialogConfirmButton"
+ android:id="@+id/letterbox_restart_dialog_restart_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="@dimen/letterbox_restart_dialog_button_width"
+ android:minHeight="@dimen/letterbox_restart_dialog_button_height"
+ android:layout_gravity="end"
+ android:background=
+ "@drawable/letterbox_restart_button_background_ripple"
+ android:text="@string/letterbox_restart_restart"
+ android:contentDescription="@string/letterbox_restart_restart"/>
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ </FrameLayout>
+
+</com.android.wm.shell.compatui.RestartDialogLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 57a8977..6919a8e 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -53,7 +53,7 @@
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Чыгуу үчүн экранды ылдый жагынан өйдө сүрүңүз же колдонмонун өйдө жагын басыңыз"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Бир кол режимин баштоо"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Бир кол режиминен чыгуу"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> калкып чыкма билдирмелер жөндөөлөрү"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> калкып чыкма билдирмелер параметрлери"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Кошумча меню"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Кайра топтомго кошуу"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосунан <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -62,7 +62,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жогорку оң жакка жылдыруу"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төмөнкү сол жакка жылдыруу"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка жылдыруу"</string>
- <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> жөндөөлөрү"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлери"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 774f6c6..76eb094 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -105,6 +105,10 @@
1.777778
</item>
+ <!-- The aspect ratio that by which optimizations to large screen sizes are made.
+ Needs to be less that or equal to 1. -->
+ <item name="config_pipLargeScreenOptimizedAspectRatio" format="float" type="dimen">0.5625</item>
+
<!-- The default gravity for the picture-in-picture window.
Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
<integer name="config_defaultPictureInPictureGravity">0x55</integer>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 3ee20ea..8908959 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -273,6 +273,39 @@
<!-- The space between two actions in the letterbox education dialog -->
<dimen name="letterbox_education_dialog_space_between_actions">24dp</dimen>
+ <!-- The margin between the dialog container and its parent. -->
+ <dimen name="letterbox_restart_dialog_margin">24dp</dimen>
+
+ <!-- The corner radius of the restart confirmation dialog. -->
+ <dimen name="letterbox_restart_dialog_corner_radius">28dp</dimen>
+
+ <!-- The fixed width of the dialog if there is enough space in the parent. -->
+ <dimen name="letterbox_restart_dialog_width">348dp</dimen>
+
+ <!-- The width of the top icon in the restart confirmation dialog. -->
+ <dimen name="letterbox_restart_dialog_title_icon_width">32dp</dimen>
+
+ <!-- The height of the top icon in the restart confirmation dialog. -->
+ <dimen name="letterbox_restart_dialog_title_icon_height">32dp</dimen>
+
+ <!-- The width of an icon in the restart confirmation dialog. -->
+ <dimen name="letterbox_restart_dialog_icon_width">40dp</dimen>
+
+ <!-- The height of an icon in the restart confirmation dialog. -->
+ <dimen name="letterbox_restart_dialog_icon_height">32dp</dimen>
+
+ <!-- The space between two actions in the restart confirmation dialog -->
+ <dimen name="letterbox_restart_dialog_space_between_actions">24dp</dimen>
+
+ <!-- The width of the buttons in the restart dialog -->
+ <dimen name="letterbox_restart_dialog_button_width">82dp</dimen>
+
+ <!-- The width of the buttons in the restart dialog -->
+ <dimen name="letterbox_restart_dialog_button_height">36dp</dimen>
+
+ <!-- The corner radius of the buttons in the restart dialog -->
+ <dimen name="letterbox_restart_dialog_button_radius">18dp</dimen>
+
<!-- The width of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_width">200dp</dimen>
@@ -331,8 +364,8 @@
<!-- Height of button (32dp) + 2 * margin (5dp each). -->
<dimen name="freeform_decor_caption_height">42dp</dimen>
- <!-- Width of buttons (64dp) + handle (128dp) + padding (24dp total). -->
- <dimen name="freeform_decor_caption_width">216dp</dimen>
+ <!-- Width of buttons (32dp each) + padding (128dp total). -->
+ <dimen name="freeform_decor_caption_menu_width">256dp</dimen>
<dimen name="freeform_resize_handle">30dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 9490ddc..3fd97ef 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -193,6 +193,23 @@
<!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] -->
<string name="letterbox_education_expand_button_description">Expand for more information.</string>
+ <!-- The title of the restart confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_restart_dialog_title">Restart for a better view?</string>
+
+ <!-- The description of the restart confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_restart_dialog_description">You can restart the app so it looks better on
+ your screen, but you may lose your progress or any unsaved changes
+ </string>
+
+ <!-- Button text for dismissing the restart confirmation dialog. [CHAR LIMIT=20] -->
+ <string name="letterbox_restart_cancel">Cancel</string>
+
+ <!-- Button text for dismissing the restart confirmation dialog. [CHAR LIMIT=20] -->
+ <string name="letterbox_restart_restart">Restart</string>
+
+ <!-- Checkbox text for asking to not show the restart confirmation dialog again. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_restart_dialog_checkbox_title">Don\u2019t show again</string>
+
<!-- Freeform window caption strings -->
<!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] -->
<string name="maximize_button_text">Maximize</string>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index a859721..e8f340c 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -63,4 +63,42 @@
<item name="android:lineHeight">16sp</item>
<item name="android:textColor">@color/tv_pip_edu_text</item>
</style>
+
+ <style name="RestartDialogTitleText">
+ <item name="android:textSize">24sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:lineSpacingExtra">2sp</item>
+ <item name="android:textAppearance">
+ @*android:style/TextAppearance.DeviceDefault.Headline
+ </item>
+ </style>
+
+ <style name="RestartDialogBodyText">
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.02</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:lineSpacingExtra">2sp</item>
+ <item name="android:textAppearance">
+ @*android:style/TextAppearance.DeviceDefault.Body2
+ </item>
+ </style>
+
+ <style name="RestartDialogCheckboxText">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:lineSpacingExtra">4sp</item>
+ <item name="android:textAppearance">@*android:style/TextAppearance.DeviceDefault</item>
+ </style>
+
+ <style name="RestartDialogDismissButton">
+ <item name="android:lineSpacingExtra">2sp</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="RestartDialogConfirmButton">
+ <item name="android:lineSpacingExtra">2sp</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+ </style>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 94e01e9..5d451a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -16,8 +16,6 @@
package com.android.wm.shell;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -30,27 +28,20 @@
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Binder;
-import android.util.CloseGuard;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewTreeObserver;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import java.io.PrintWriter;
import java.util.concurrent.Executor;
/**
- * View that can display a task.
+ * A {@link SurfaceView} that can display a task. This is a concrete implementation for
+ * {@link TaskViewBase} which interacts {@link TaskViewTaskController}.
*/
public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
- ShellTaskOrganizer.TaskListener, ViewTreeObserver.OnComputeInternalInsetsListener {
-
+ ViewTreeObserver.OnComputeInternalInsetsListener, TaskViewBase {
/** Callback for listening task state. */
public interface Listener {
/**
@@ -75,65 +66,33 @@
default void onBackPressedOnTaskRoot(int taskId) {}
}
- private final CloseGuard mGuard = new CloseGuard();
-
- private final ShellTaskOrganizer mTaskOrganizer;
- private final Executor mShellExecutor;
- private final SyncTransactionQueue mSyncQueue;
- private final TaskViewTransitions mTaskViewTransitions;
-
- protected ActivityManager.RunningTaskInfo mTaskInfo;
- private WindowContainerToken mTaskToken;
- private SurfaceControl mTaskLeash;
- private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
- private boolean mSurfaceCreated;
- private boolean mIsInitialized;
- private boolean mNotifiedForInitialized;
- private Listener mListener;
- private Executor mListenerExecutor;
- private Region mObscuredTouchRegion;
-
private final Rect mTmpRect = new Rect();
private final Rect mTmpRootRect = new Rect();
private final int[] mTmpLocation = new int[2];
+ private final TaskViewTaskController mTaskViewTaskController;
+ private Region mObscuredTouchRegion;
- public TaskView(Context context, ShellTaskOrganizer organizer,
- TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) {
+ public TaskView(Context context, TaskViewTaskController taskViewTaskController) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
-
- mTaskOrganizer = organizer;
- mShellExecutor = organizer.getExecutor();
- mSyncQueue = syncQueue;
- mTaskViewTransitions = taskViewTransitions;
- if (mTaskViewTransitions != null) {
- mTaskViewTransitions.addTaskView(this);
- }
+ mTaskViewTaskController = taskViewTaskController;
+ // TODO(b/266736992): Think about a better way to set the TaskViewBase on the
+ // TaskViewTaskController and vice-versa
+ mTaskViewTaskController.setTaskViewBase(this);
getHolder().addCallback(this);
- mGuard.open("release");
}
/**
- * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise.
+ * Launch a new activity.
+ *
+ * @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+ * @param options options for the activity.
+ * @param launchBounds the bounds (window size and position) that the activity should be
+ * launched in, in pixels and in screen coordinates.
*/
- public boolean isInitialized() {
- return mIsInitialized;
- }
-
- /** Until all users are converted, we may have mixed-use (eg. Car). */
- private boolean isUsingShellTransitions() {
- return mTaskViewTransitions != null && mTaskViewTransitions.isEnabled();
- }
-
- /**
- * Only one listener may be set on the view, throws an exception otherwise.
- */
- public void setListener(@NonNull Executor executor, Listener listener) {
- if (mListener != null) {
- throw new IllegalStateException(
- "Trying to set a listener when one has already been set");
- }
- mListener = listener;
- mListenerExecutor = executor;
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+ @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+ mTaskViewTaskController.startActivity(pendingIntent, fillInIntent, options, launchBounds);
}
/**
@@ -148,61 +107,47 @@
*/
public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
- prepareActivityOptions(options, launchBounds);
- LauncherApps service = mContext.getSystemService(LauncherApps.class);
- if (isUsingShellTransitions()) {
- mShellExecutor.execute(() -> {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.startShortcut(mContext.getPackageName(), shortcut, options.toBundle());
- mTaskViewTransitions.startTaskView(wct, this);
- });
- return;
+ mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds);
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ onLocationChanged();
+ if (taskInfo.taskDescription != null) {
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
}
- try {
- service.startShortcut(shortcut, null /* sourceBounds */, options.toBundle());
- } catch (Exception e) {
- throw new RuntimeException(e);
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.taskDescription != null) {
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
}
}
/**
- * Launch a new activity.
- *
- * @param pendingIntent Intent used to launch an activity.
- * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
- * @param options options for the activity.
- * @param launchBounds the bounds (window size and position) that the activity should be
- * launched in, in pixels and in screen coordinates.
+ * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise.
*/
- public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
- @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
- prepareActivityOptions(options, launchBounds);
- if (isUsingShellTransitions()) {
- mShellExecutor.execute(() -> {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.sendPendingIntent(pendingIntent, fillInIntent, options.toBundle());
- mTaskViewTransitions.startTaskView(wct, this);
- });
- return;
- }
- try {
- pendingIntent.send(mContext, 0 /* code */, fillInIntent,
- null /* onFinished */, null /* handler */, null /* requiredPermission */,
- options.toBundle());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ public boolean isInitialized() {
+ return mTaskViewTaskController.isInitialized();
}
- private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
- final Binder launchCookie = new Binder();
- mShellExecutor.execute(() -> {
- mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
- });
- options.setLaunchBounds(launchBounds);
- options.setLaunchCookie(launchCookie);
- options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- options.setRemoveWithTaskOrganizer(true);
+ @Override
+ public Rect getCurrentBoundsOnScreen() {
+ getBoundsOnScreen(mTmpRect);
+ return mTmpRect;
+ }
+
+ @Override
+ public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
+ setResizeBackgroundColor(t, bgColor);
+ }
+
+ /**
+ * Only one listener may be set on the view, throws an exception otherwise.
+ */
+ public void setListener(@NonNull Executor executor, TaskView.Listener listener) {
+ mTaskViewTaskController.setListener(executor, listener);
}
/**
@@ -227,251 +172,38 @@
* Call when view position or size has changed. Do not call when animating.
*/
public void onLocationChanged() {
- if (mTaskToken == null) {
- return;
- }
- // Sync Transactions can't operate simultaneously with shell transition collection.
- // The transition animation (upon showing) will sync the location itself.
- if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
-
- WindowContainerTransaction wct = new WindowContainerTransaction();
- updateWindowBounds(wct);
- mSyncQueue.queue(wct);
- }
-
- private void updateWindowBounds(WindowContainerTransaction wct) {
getBoundsOnScreen(mTmpRect);
- wct.setBounds(mTaskToken, mTmpRect);
+ mTaskViewTaskController.onLocationChanged(mTmpRect);
}
/**
* Release this container if it is initialized.
*/
public void release() {
- performRelease();
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mGuard != null) {
- mGuard.warnIfOpen();
- performRelease();
- }
- } finally {
- super.finalize();
- }
- }
-
- private void performRelease() {
getHolder().removeCallback(this);
- if (mTaskViewTransitions != null) {
- mTaskViewTransitions.removeTaskView(this);
- }
- mShellExecutor.execute(() -> {
- mTaskOrganizer.removeListener(this);
- resetTaskInfo();
- });
- mGuard.close();
- mIsInitialized = false;
- notifyReleased();
- }
-
- /** Called when the {@link TaskView} has been released. */
- protected void notifyReleased() {
- if (mListener != null && mNotifiedForInitialized) {
- mListenerExecutor.execute(() -> {
- mListener.onReleased();
- });
- mNotifiedForInitialized = false;
- }
- }
-
- private void resetTaskInfo() {
- mTaskInfo = null;
- mTaskToken = null;
- mTaskLeash = null;
- }
-
- private void updateTaskVisibility() {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
- mSyncQueue.queue(wct);
- if (mListener == null) {
- return;
- }
- int taskId = mTaskInfo.taskId;
- mSyncQueue.runInSync((t) -> {
- mListenerExecutor.execute(() -> {
- mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
- });
- });
- }
-
- @Override
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl leash) {
- if (isUsingShellTransitions()) {
- // Everything else handled by enter transition.
- return;
- }
- mTaskInfo = taskInfo;
- mTaskToken = taskInfo.token;
- mTaskLeash = leash;
-
- if (mSurfaceCreated) {
- // Surface is ready, so just reparent the task to this surface control
- mTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- } else {
- // The surface has already been destroyed before the task has appeared,
- // so go ahead and hide the task entirely
- updateTaskVisibility();
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
- onLocationChanged();
- if (taskInfo.taskDescription != null) {
- int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
- mSyncQueue.runInSync((t) -> {
- setResizeBackgroundColor(t, backgroundColor);
- });
- }
-
- if (mListener != null) {
- final int taskId = taskInfo.taskId;
- final ComponentName baseActivity = taskInfo.baseActivity;
- mListenerExecutor.execute(() -> {
- mListener.onTaskCreated(taskId, baseActivity);
- });
- }
- }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- // Unlike Appeared, we can't yet guarantee that vanish will happen within a transition that
- // we know about -- so leave clean-up here even if shell transitions are enabled.
- if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
-
- if (mListener != null) {
- final int taskId = taskInfo.taskId;
- mListenerExecutor.execute(() -> {
- mListener.onTaskRemovalStarted(taskId);
- });
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
-
- // Unparent the task when this surface is destroyed
- mTransaction.reparent(mTaskLeash, null).apply();
- resetTaskInfo();
- }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
- }
- }
-
- @Override
- public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
- if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
- if (mListener != null) {
- final int taskId = taskInfo.taskId;
- mListenerExecutor.execute(() -> {
- mListener.onBackPressedOnTaskRoot(taskId);
- });
- }
- }
-
- @Override
- public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- b.setParent(findTaskSurface(taskId));
- }
-
- @Override
- public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
- SurfaceControl.Transaction t) {
- t.reparent(sc, findTaskSurface(taskId));
- }
-
- private SurfaceControl findTaskSurface(int taskId) {
- if (mTaskInfo == null || mTaskLeash == null || mTaskInfo.taskId != taskId) {
- throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
- }
- return mTaskLeash;
- }
-
- @Override
- public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + this);
+ mTaskViewTaskController.release();
}
@Override
public String toString() {
- return "TaskView" + ":" + (mTaskInfo != null ? mTaskInfo.taskId : "null");
+ return mTaskViewTaskController.toString();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
- mSurfaceCreated = true;
- mIsInitialized = true;
- notifyInitialized();
- mShellExecutor.execute(() -> {
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
- if (isUsingShellTransitions()) {
- mTaskViewTransitions.setTaskViewVisible(this, true /* visible */);
- return;
- }
- // Reparent the task when this surface is created
- mTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- updateTaskVisibility();
- });
- }
-
- /** Called when the {@link TaskView} is initialized. */
- protected void notifyInitialized() {
- if (mListener != null && !mNotifiedForInitialized) {
- mNotifiedForInitialized = true;
- mListenerExecutor.execute(() -> {
- mListener.onInitialized();
- });
- }
+ mTaskViewTaskController.surfaceCreated(getSurfaceControl());
}
@Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- if (mTaskToken == null) {
- return;
- }
- onLocationChanged();
+ public void surfaceChanged(@androidx.annotation.NonNull SurfaceHolder holder, int format,
+ int width, int height) {
+ getBoundsOnScreen(mTmpRect);
+ mTaskViewTaskController.surfaceChanged(mTmpRect);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
- mSurfaceCreated = false;
- mShellExecutor.execute(() -> {
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
-
- if (isUsingShellTransitions()) {
- mTaskViewTransitions.setTaskViewVisible(this, false /* visible */);
- return;
- }
-
- // Unparent the task when this surface is destroyed
- mTransaction.reparent(mTaskLeash, null).apply();
- updateTaskVisibility();
- });
+ mTaskViewTaskController.surfaceDestroyed();
}
@Override
@@ -513,89 +245,6 @@
/** Returns the task info for the task in the TaskView. */
@Nullable
public ActivityManager.RunningTaskInfo getTaskInfo() {
- return mTaskInfo;
- }
-
- void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
-
- finishTransaction.reparent(mTaskLeash, null).apply();
-
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
- mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
- }
- }
-
- /**
- * Called when the associated Task closes. If the TaskView is just being hidden, prepareHide
- * is used instead.
- */
- void prepareCloseAnimation() {
- if (mTaskToken != null) {
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
- mListenerExecutor.execute(() -> {
- mListener.onTaskRemovalStarted(taskId);
- });
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
- }
- resetTaskInfo();
- }
-
- void prepareOpenAnimation(final boolean newTask,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
- WindowContainerTransaction wct) {
- mTaskInfo = taskInfo;
- mTaskToken = mTaskInfo.token;
- mTaskLeash = leash;
- if (mSurfaceCreated) {
- // Surface is ready, so just reparent the task to this surface control
- startTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- // Also reparent on finishTransaction since the finishTransaction will reparent back
- // to its "original" parent by default.
- finishTransaction.reparent(mTaskLeash, getSurfaceControl())
- .setPosition(mTaskLeash, 0, 0)
- .apply();
-
- // TODO: determine if this is really necessary or not
- updateWindowBounds(wct);
- } else {
- // The surface has already been destroyed before the task has appeared,
- // so go ahead and hide the task entirely
- wct.setHidden(mTaskToken, true /* hidden */);
- // listener callback is below
- }
- if (newTask) {
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true /* intercept */);
- }
-
- if (mTaskInfo.taskDescription != null) {
- int backgroundColor = mTaskInfo.taskDescription.getBackgroundColor();
- setResizeBackgroundColor(startTransaction, backgroundColor);
- }
-
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
- final ComponentName baseActivity = mTaskInfo.baseActivity;
-
- mListenerExecutor.execute(() -> {
- if (newTask) {
- mListener.onTaskCreated(taskId, baseActivity);
- }
- // Even if newTask, send a visibilityChange if the surface was destroyed.
- if (!newTask || !mSurfaceCreated) {
- mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
- }
- });
- }
+ return mTaskViewTaskController.getTaskInfo();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewBase.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewBase.java
new file mode 100644
index 0000000..3d0a8fd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewBase.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+/**
+ * A stub for SurfaceView used by {@link TaskViewTaskController}
+ */
+public interface TaskViewBase {
+ /**
+ * Returns the current bounds on screen for the task view.
+ * @return
+ */
+ // TODO(b/266242294): Remove getBoundsOnScreen() and instead send the bounds from the TaskView
+ // to TaskViewTaskController.
+ Rect getCurrentBoundsOnScreen();
+
+ /**
+ * This method should set the resize background color on the SurfaceView that is exposed to
+ * clients.
+ * See {@link android.view.SurfaceView#setResizeBackgroundColor(SurfaceControl.Transaction,
+ * int)}
+ */
+ void setResizeBgColor(SurfaceControl.Transaction transaction, int color);
+
+ /**
+ * Called when a task appears on the TaskView. See
+ * {@link TaskViewTaskController#onTaskAppeared(ActivityManager.RunningTaskInfo,
+ * SurfaceControl)} for details.
+ */
+ default void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ }
+
+ /**
+ * Called when a task is vanished from the TaskView. See
+ * {@link TaskViewTaskController#onTaskVanished(ActivityManager.RunningTaskInfo)} for details.
+ */
+ default void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ }
+
+ /**
+ * Called when the task in the TaskView is changed. See
+ * {@link TaskViewTaskController#onTaskInfoChanged(ActivityManager.RunningTaskInfo)} for details.
+ */
+ default void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 42844b5..735d9bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -57,7 +57,8 @@
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
- TaskView taskView = new TaskView(context, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
+ TaskView taskView = new TaskView(context, new TaskViewTaskController(context,
+ mTaskOrganizer, mTaskViewTransitions, mSyncQueue));
executor.execute(() -> {
onCreate.accept(taskView);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTaskController.java
new file mode 100644
index 0000000..69ce35f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTaskController.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.CloseGuard;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+/**
+ * This class implements the core logic to show a task on the {@link TaskView}. All the {@link
+ * TaskView} to {@link TaskViewTaskController} interactions are done via direct method calls.
+ *
+ * The reverse communication is done via the {@link TaskViewBase} interface.
+ */
+public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
+
+ private final CloseGuard mGuard = new CloseGuard();
+
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final Executor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
+ private final TaskViewTransitions mTaskViewTransitions;
+ private TaskViewBase mTaskViewBase;
+ private final Context mContext;
+
+ protected ActivityManager.RunningTaskInfo mTaskInfo;
+ private WindowContainerToken mTaskToken;
+ private SurfaceControl mTaskLeash;
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ private boolean mSurfaceCreated;
+ private SurfaceControl mSurfaceControl;
+ private boolean mIsInitialized;
+ private boolean mNotifiedForInitialized;
+ private TaskView.Listener mListener;
+ private Executor mListenerExecutor;
+
+ public TaskViewTaskController(Context context, ShellTaskOrganizer organizer,
+ TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) {
+ mContext = context;
+ mTaskOrganizer = organizer;
+ mShellExecutor = organizer.getExecutor();
+ mSyncQueue = syncQueue;
+ mTaskViewTransitions = taskViewTransitions;
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.addTaskView(this);
+ }
+ mGuard.open("release");
+ }
+
+ /**
+ * Sets the provided {@link TaskViewBase}, which is used to notify the client part about the
+ * task related changes and getting the current bounds.
+ */
+ public void setTaskViewBase(TaskViewBase taskViewBase) {
+ mTaskViewBase = taskViewBase;
+ }
+
+ /**
+ * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise.
+ */
+ public boolean isInitialized() {
+ return mIsInitialized;
+ }
+
+ /** Until all users are converted, we may have mixed-use (eg. Car). */
+ private boolean isUsingShellTransitions() {
+ return mTaskViewTransitions != null && mTaskViewTransitions.isEnabled();
+ }
+
+ /**
+ * Only one listener may be set on the view, throws an exception otherwise.
+ */
+ void setListener(@NonNull Executor executor, TaskView.Listener listener) {
+ if (mListener != null) {
+ throw new IllegalStateException(
+ "Trying to set a listener when one has already been set");
+ }
+ mListener = listener;
+ mListenerExecutor = executor;
+ }
+
+ /**
+ * Launch an activity represented by {@link ShortcutInfo}.
+ * <p>The owner of this container must be allowed to access the shortcut information,
+ * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+ *
+ * @param shortcut the shortcut used to launch the activity.
+ * @param options options for the activity.
+ * @param launchBounds the bounds (window size and position) that the activity should be
+ * launched in, in pixels and in screen coordinates.
+ */
+ public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+ @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+ prepareActivityOptions(options, launchBounds);
+ LauncherApps service = mContext.getSystemService(LauncherApps.class);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.startShortcut(mContext.getPackageName(), shortcut, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
+ try {
+ service.startShortcut(shortcut, null /* sourceBounds */, options.toBundle());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Launch a new activity.
+ *
+ * @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+ * @param options options for the activity.
+ * @param launchBounds the bounds (window size and position) that the activity should be
+ * launched in, in pixels and in screen coordinates.
+ */
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+ @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+ prepareActivityOptions(options, launchBounds);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.sendPendingIntent(pendingIntent, fillInIntent, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
+ try {
+ pendingIntent.send(mContext, 0 /* code */, fillInIntent,
+ null /* onFinished */, null /* handler */, null /* requiredPermission */,
+ options.toBundle());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
+ final Binder launchCookie = new Binder();
+ mShellExecutor.execute(() -> {
+ mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
+ });
+ options.setLaunchBounds(launchBounds);
+ options.setLaunchCookie(launchCookie);
+ options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ options.setRemoveWithTaskOrganizer(true);
+ }
+
+ /**
+ * Call when view position or size has changed. Do not call when animating.
+ */
+ public void onLocationChanged(Rect newBounds) {
+ if (mTaskToken == null) {
+ return;
+ }
+ // Sync Transactions can't operate simultaneously with shell transition collection.
+ // The transition animation (upon showing) will sync the location itself.
+ if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ updateWindowBounds(wct);
+ mSyncQueue.queue(wct);
+ }
+
+ private void updateWindowBounds(WindowContainerTransaction wct) {
+ wct.setBounds(mTaskToken, mTaskViewBase.getCurrentBoundsOnScreen());
+ }
+
+ /**
+ * Release this container if it is initialized.
+ */
+ public void release() {
+ performRelease();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ performRelease();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void performRelease() {
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.removeTaskView(this);
+ }
+ mShellExecutor.execute(() -> {
+ mTaskOrganizer.removeListener(this);
+ resetTaskInfo();
+ });
+ mGuard.close();
+ mIsInitialized = false;
+ notifyReleased();
+ }
+
+ /** Called when the {@link TaskViewTaskController} has been released. */
+ protected void notifyReleased() {
+ if (mListener != null && mNotifiedForInitialized) {
+ mListenerExecutor.execute(() -> {
+ mListener.onReleased();
+ });
+ mNotifiedForInitialized = false;
+ }
+ }
+
+ private void resetTaskInfo() {
+ mTaskInfo = null;
+ mTaskToken = null;
+ mTaskLeash = null;
+ }
+
+ private void updateTaskVisibility() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
+ mSyncQueue.queue(wct);
+ if (mListener == null) {
+ return;
+ }
+ int taskId = mTaskInfo.taskId;
+ mSyncQueue.runInSync((t) -> {
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
+ });
+ });
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl leash) {
+ if (isUsingShellTransitions()) {
+ // Everything else handled by enter transition.
+ return;
+ }
+ mTaskInfo = taskInfo;
+ mTaskToken = taskInfo.token;
+ mTaskLeash = leash;
+
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ mTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .show(mTaskLeash)
+ .apply();
+ } else {
+ // The surface has already been destroyed before the task has appeared,
+ // so go ahead and hide the task entirely
+ updateTaskVisibility();
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
+ mSyncQueue.runInSync((t) -> {
+ mTaskViewBase.onTaskAppeared(taskInfo, leash);
+ });
+
+ if (mListener != null) {
+ final int taskId = taskInfo.taskId;
+ final ComponentName baseActivity = taskInfo.baseActivity;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskCreated(taskId, baseActivity);
+ });
+ }
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ // Unlike Appeared, we can't yet guarantee that vanish will happen within a transition that
+ // we know about -- so leave clean-up here even if shell transitions are enabled.
+ if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+
+ if (mListener != null) {
+ final int taskId = taskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskRemovalStarted(taskId);
+ });
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ resetTaskInfo();
+ mTaskViewBase.onTaskVanished(taskInfo);
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskViewBase.onTaskInfoChanged(taskInfo);
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+ if (mListener != null) {
+ final int taskId = taskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onBackPressedOnTaskRoot(taskId);
+ });
+ }
+ }
+
+ @Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
+ if (mTaskInfo == null || mTaskLeash == null || mTaskInfo.taskId != taskId) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ return mTaskLeash;
+ }
+
+ @Override
+ public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+ }
+
+ @Override
+ public String toString() {
+ return "TaskViewTaskController" + ":" + (mTaskInfo != null ? mTaskInfo.taskId : "null");
+ }
+
+ /**
+ * Should be called when the client surface is created.
+ *
+ * @param surfaceControl the {@link SurfaceControl} for the underlying surface.
+ */
+ public void surfaceCreated(SurfaceControl surfaceControl) {
+ mSurfaceCreated = true;
+ mIsInitialized = true;
+ mSurfaceControl = surfaceControl;
+ notifyInitialized();
+ mShellExecutor.execute(() -> {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, true /* visible */);
+ return;
+ }
+ // Reparent the task when this surface is created
+ mTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .show(mTaskLeash)
+ .apply();
+ updateTaskVisibility();
+ });
+ }
+
+ /**
+ * Should be called when the client surface is changed.
+ *
+ * @param boundsOnScreen the on screen bounds of the surface view.
+ */
+ public void surfaceChanged(Rect boundsOnScreen) {
+ if (mTaskToken == null) {
+ return;
+ }
+ onLocationChanged(boundsOnScreen);
+ }
+
+ /** Should be called when the client surface is destroyed. */
+ public void surfaceDestroyed() {
+ mSurfaceCreated = false;
+ mSurfaceControl = null;
+ mShellExecutor.execute(() -> {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, false /* visible */);
+ return;
+ }
+
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ updateTaskVisibility();
+ });
+ }
+
+ /** Called when the {@link TaskViewTaskController} is initialized. */
+ protected void notifyInitialized() {
+ if (mListener != null && !mNotifiedForInitialized) {
+ mNotifiedForInitialized = true;
+ mListenerExecutor.execute(() -> {
+ mListener.onInitialized();
+ });
+ }
+ }
+
+ /** Returns the task info for the task in the TaskView. */
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+
+ finishTransaction.reparent(mTaskLeash, null).apply();
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ }
+
+ /**
+ * Called when the associated Task closes. If the TaskView is just being hidden, prepareHide
+ * is used instead.
+ */
+ void prepareCloseAnimation() {
+ if (mTaskToken != null) {
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskRemovalStarted(taskId);
+ });
+ }
+ mTaskViewBase.onTaskVanished(mTaskInfo);
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ }
+ resetTaskInfo();
+ }
+
+ void prepareOpenAnimation(final boolean newTask,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ WindowContainerTransaction wct) {
+ mTaskInfo = taskInfo;
+ mTaskToken = mTaskInfo.token;
+ mTaskLeash = leash;
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ startTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .show(mTaskLeash)
+ .apply();
+ // Also reparent on finishTransaction since the finishTransaction will reparent back
+ // to its "original" parent by default.
+ finishTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .setPosition(mTaskLeash, 0, 0)
+ .apply();
+
+ updateWindowBounds(wct);
+ } else {
+ // The surface has already been destroyed before the task has appeared,
+ // so go ahead and hide the task entirely
+ wct.setHidden(mTaskToken, true /* hidden */);
+ // listener callback is below
+ }
+ if (newTask) {
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true /* intercept */);
+ }
+
+ if (mTaskInfo.taskDescription != null) {
+ int backgroundColor = mTaskInfo.taskDescription.getBackgroundColor();
+ mTaskViewBase.setResizeBgColor(startTransaction, backgroundColor);
+ }
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ final ComponentName baseActivity = mTaskInfo.baseActivity;
+
+ mListenerExecutor.execute(() -> {
+ if (newTask) {
+ mListener.onTaskCreated(taskId, baseActivity);
+ }
+ // Even if newTask, send a visibilityChange if the surface was destroyed.
+ if (!newTask || !mSurfaceCreated) {
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ });
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
index 07d5012..aab257e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
@@ -41,7 +41,7 @@
public class TaskViewTransitions implements Transitions.TransitionHandler {
private static final String TAG = "TaskViewTransitions";
- private final ArrayList<TaskView> mTaskViews = new ArrayList<>();
+ private final ArrayList<TaskViewTaskController> mTaskViews = new ArrayList<>();
private final ArrayList<PendingTransition> mPending = new ArrayList<>();
private final Transitions mTransitions;
private final boolean[] mRegistered = new boolean[]{ false };
@@ -54,11 +54,12 @@
private static class PendingTransition {
final @WindowManager.TransitionType int mType;
final WindowContainerTransaction mWct;
- final @NonNull TaskView mTaskView;
+ final @NonNull TaskViewTaskController mTaskView;
IBinder mClaimed;
PendingTransition(@WindowManager.TransitionType int type,
- @Nullable WindowContainerTransaction wct, @NonNull TaskView taskView) {
+ @Nullable WindowContainerTransaction wct,
+ @NonNull TaskViewTaskController taskView) {
mType = type;
mWct = wct;
mTaskView = taskView;
@@ -72,7 +73,7 @@
// TODO(210041388): register here once we have an explicit ordering mechanism.
}
- void addTaskView(TaskView tv) {
+ void addTaskView(TaskViewTaskController tv) {
synchronized (mRegistered) {
if (!mRegistered[0]) {
mRegistered[0] = true;
@@ -82,7 +83,7 @@
mTaskViews.add(tv);
}
- void removeTaskView(TaskView tv) {
+ void removeTaskView(TaskViewTaskController tv) {
mTaskViews.remove(tv);
// Note: Don't unregister handler since this is a singleton with lifetime bound to Shell
}
@@ -101,7 +102,8 @@
* if there is a match earlier. The idea behind this is to check the state of
* the taskviews "as if all transitions already happened".
*/
- private PendingTransition findPending(TaskView taskView, boolean closing, boolean latest) {
+ private PendingTransition findPending(TaskViewTaskController taskView, boolean closing,
+ boolean latest) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
if (Transitions.isClosingType(mPending.get(i).mType) == closing) {
@@ -134,7 +136,7 @@
if (triggerTask == null) {
return null;
}
- final TaskView taskView = findTaskView(triggerTask);
+ final TaskViewTaskController taskView = findTaskView(triggerTask);
if (taskView == null) return null;
// Opening types should all be initiated by shell
if (!Transitions.isClosingType(request.getType())) return null;
@@ -150,7 +152,7 @@
return new WindowContainerTransaction();
}
- private TaskView findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
+ private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
for (int i = 0; i < mTaskViews.size(); ++i) {
if (mTaskViews.get(i).getTaskInfo() == null) continue;
if (taskInfo.token.equals(mTaskViews.get(i).getTaskInfo().token)) {
@@ -160,12 +162,12 @@
return null;
}
- void startTaskView(WindowContainerTransaction wct, TaskView taskView) {
+ void startTaskView(WindowContainerTransaction wct, TaskViewTaskController taskView) {
mPending.add(new PendingTransition(TRANSIT_OPEN, wct, taskView));
startNextTransition();
}
- void setTaskViewVisible(TaskView taskView, boolean visible) {
+ void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
PendingTransition pending = findPending(taskView, !visible, true /* latest */);
if (pending != null) {
// Already opening or creating a task, so no need to do anything here.
@@ -203,7 +205,7 @@
PendingTransition pending = findPending(transition);
if (pending == null) return false;
mPending.remove(pending);
- TaskView taskView = pending.mTaskView;
+ TaskViewTaskController taskView = pending.mTaskView;
final ArrayList<TransitionInfo.Change> tasks = new ArrayList<>();
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change chg = info.getChanges().get(i);
@@ -219,7 +221,7 @@
TransitionInfo.Change chg = tasks.get(i);
if (Transitions.isClosingType(chg.getMode())) {
final boolean isHide = chg.getMode() == TRANSIT_TO_BACK;
- TaskView tv = findTaskView(chg.getTaskInfo());
+ TaskViewTaskController tv = findTaskView(chg.getTaskInfo());
if (tv == null) {
throw new IllegalStateException("TaskView transition is closing a "
+ "non-taskview task ");
@@ -232,7 +234,7 @@
} else if (Transitions.isOpeningType(chg.getMode())) {
final boolean taskIsNew = chg.getMode() == TRANSIT_OPEN;
if (wct == null) wct = new WindowContainerTransaction();
- TaskView tv = taskView;
+ TaskViewTaskController tv = taskView;
if (!taskIsNew) {
tv = findTaskView(chg.getTaskInfo());
if (tv == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 09dc68a..2534498 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -21,6 +21,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import android.annotation.DimenRes;
+import android.annotation.Hide;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -180,7 +181,7 @@
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
- int taskId, @Nullable final String locus, Executor mainExecutor,
+ int taskId, @Nullable final String locus, boolean isClearable, Executor mainExecutor,
final Bubbles.BubbleMetadataFlagListener listener) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
@@ -189,6 +190,7 @@
mKey = key;
mGroupKey = null;
mLocusId = locus != null ? new LocusId(locus) : null;
+ mIsClearable = isClearable;
mFlags = 0;
mUser = shortcutInfo.getUserHandle();
mPackageName = shortcutInfo.getPackage();
@@ -245,6 +247,11 @@
return mKey;
}
+ @Hide
+ public boolean isClearable() {
+ return mIsClearable;
+ }
+
/**
* @see StatusBarNotification#getGroupKey()
* @return the group key for this bubble, if one exists.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 3a59614..e3aefa5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -109,7 +109,8 @@
b.rawDesiredHeightResId,
b.title,
b.taskId,
- b.locusId?.id
+ b.locusId?.id,
+ b.isClearable
)
}
}
@@ -205,6 +206,7 @@
entity.title,
entity.taskId,
entity.locus,
+ entity.isClearable,
mainExecutor,
bubbleMetadataFlagListener
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index e85b3c7..1feff18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -67,6 +67,7 @@
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.TaskView;
+import com.android.wm.shell.TaskViewTaskController;
import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.common.TriangleShape;
@@ -140,6 +141,7 @@
private AlphaOptimizedButton mManageButton;
private TaskView mTaskView;
+ private TaskViewTaskController mTaskViewTaskController;
private BubbleOverflowContainerView mOverflowView;
private int mTaskId = INVALID_TASK_ID;
@@ -409,8 +411,10 @@
bringChildToFront(mOverflowView);
mManageButton.setVisibility(GONE);
} else {
- mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
+ mTaskViewTaskController = new TaskViewTaskController(mContext,
+ mController.getTaskOrganizer(),
mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
+ mTaskView = new TaskView(mContext, mTaskViewTaskController);
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
index 186b9b1..f3abc27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
@@ -27,5 +27,6 @@
@DimenRes val desiredHeightResId: Int,
val title: String? = null,
val taskId: Int,
- val locus: String? = null
+ val locus: String? = null,
+ val isClearable: Boolean = false
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
index f4fa183..14c053c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
@@ -44,6 +44,9 @@
private const val ATTR_TASK_ID = "tid"
private const val ATTR_LOCUS = "l"
+// TODO rename it to dismissable to follow NotificationEntry namings
+private const val ATTR_CLEARABLE = "d"
+
/**
* Writes the bubbles in xml format into given output stream.
*/
@@ -84,6 +87,7 @@
bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) }
serializer.attribute(null, ATTR_TASK_ID, bubble.taskId.toString())
bubble.locus?.let { serializer.attribute(null, ATTR_LOCUS, it) }
+ serializer.attribute(null, ATTR_CLEARABLE, bubble.isClearable.toString())
serializer.endTag(null, TAG_BUBBLE)
} catch (e: IOException) {
throw RuntimeException(e)
@@ -142,7 +146,8 @@
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_TITLE),
parser.getAttributeWithName(ATTR_TASK_ID)?.toInt() ?: INVALID_TASK_ID,
- parser.getAttributeWithName(ATTR_LOCUS)
+ parser.getAttributeWithName(ATTR_LOCUS),
+ parser.getAttributeWithName(ATTR_CLEARABLE)?.toBoolean() ?: false
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
index 4f33a71..06f0a70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
@@ -16,11 +16,12 @@
package com.android.wm.shell.compatui;
+import android.annotation.NonNull;
+import android.app.TaskInfo;
import android.content.Context;
+import android.content.SharedPreferences;
import android.provider.DeviceConfig;
-import androidx.annotation.NonNull;
-
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -34,11 +35,27 @@
@WMSingleton
public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedListener {
- static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG = "enable_letterbox_restart_dialog";
+ private static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG =
+ "enable_letterbox_restart_confirmation_dialog";
- static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
+ private static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
"enable_letterbox_reachability_education";
+ private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG = true;
+
+ private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION = false;
+
+ /**
+ * The name of the {@link SharedPreferences} that holds which user has seen the Restart
+ * confirmation dialog.
+ */
+ private static final String DONT_SHOW_RESTART_DIALOG_PREF_NAME = "dont_show_restart_dialog";
+
+ /**
+ * The {@link SharedPreferences} instance for {@link #DONT_SHOW_RESTART_DIALOG_PREF_NAME}.
+ */
+ private final SharedPreferences mSharedPreferences;
+
// Whether the extended restart dialog is enabled
private boolean mIsRestartDialogEnabled;
@@ -64,12 +81,15 @@
mIsReachabilityEducationEnabled = context.getResources().getBoolean(
R.bool.config_letterboxIsReachabilityEducationEnabled);
mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG, false);
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
+ DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG);
mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION,
- false);
+ DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor,
this);
+ mSharedPreferences = context.getSharedPreferences(DONT_SHOW_RESTART_DIALOG_PREF_NAME,
+ Context.MODE_PRIVATE);
}
/**
@@ -102,18 +122,37 @@
mIsReachabilityEducationOverrideEnabled = enabled;
}
+ boolean getDontShowRestartDialogAgain(TaskInfo taskInfo) {
+ final int userId = taskInfo.userId;
+ final String packageName = taskInfo.topActivity.getPackageName();
+ return mSharedPreferences.getBoolean(
+ getDontShowAgainRestartKey(userId, packageName), /* default= */ false);
+ }
+
+ void setDontShowRestartDialogAgain(TaskInfo taskInfo) {
+ final int userId = taskInfo.userId;
+ final String packageName = taskInfo.topActivity.getPackageName();
+ mSharedPreferences.edit().putBoolean(getDontShowAgainRestartKey(userId, packageName),
+ true).apply();
+ }
+
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
- // TODO(b/263349751): Update flag and default value to true
if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_RESTART_DIALOG)) {
mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
- false);
+ DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG);
}
+ // TODO(b/263349751): Update flag and default value to true
if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION)) {
mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION, false);
+ KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION,
+ DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION);
}
}
-}
+
+ private String getDontShowAgainRestartKey(int userId, String packageName) {
+ return packageName + "@" + userId;
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 6627de5..3b2db51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -24,6 +24,7 @@
import android.hardware.display.DisplayManager;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.view.Display;
import android.view.InsetsSourceControl;
@@ -49,6 +50,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -91,6 +93,18 @@
private final SparseArray<CompatUIWindowManager> mActiveCompatLayouts = new SparseArray<>(0);
/**
+ * {@link SparseArray} that maps task ids to {@link RestartDialogWindowManager} that are
+ * currently visible
+ */
+ private final SparseArray<RestartDialogWindowManager> mTaskIdToRestartDialogWindowManagerMap =
+ new SparseArray<>(0);
+
+ /**
+ * {@link Set} of task ids for which we need to display a restart confirmation dialog
+ */
+ private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>();
+
+ /**
* The active Letterbox Education layout if there is one (there can be at most one active).
*
* <p>An active layout is a layout that is eligible to be shown for the associated task but
@@ -111,12 +125,15 @@
private final ShellExecutor mMainExecutor;
private final Lazy<Transitions> mTransitionsLazy;
private final DockStateReader mDockStateReader;
+ private final CompatUIConfiguration mCompatUIConfiguration;
private CompatUICallback mCallback;
// Only show each hint once automatically in the process life.
private final CompatUIHintsState mCompatUIHintsState;
+ private final CompatUIShellCommandHandler mCompatUIShellCommandHandler;
+
// Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
// be shown.
private boolean mKeyguardShowing;
@@ -130,7 +147,9 @@
SyncTransactionQueue syncQueue,
ShellExecutor mainExecutor,
Lazy<Transitions> transitionsLazy,
- DockStateReader dockStateReader) {
+ DockStateReader dockStateReader,
+ CompatUIConfiguration compatUIConfiguration,
+ CompatUIShellCommandHandler compatUIShellCommandHandler) {
mContext = context;
mShellController = shellController;
mDisplayController = displayController;
@@ -140,14 +159,17 @@
mMainExecutor = mainExecutor;
mTransitionsLazy = transitionsLazy;
mCompatUIHintsState = new CompatUIHintsState();
- shellInit.addInitCallback(this::onInit, this);
mDockStateReader = dockStateReader;
+ mCompatUIConfiguration = compatUIConfiguration;
+ mCompatUIShellCommandHandler = compatUIShellCommandHandler;
+ shellInit.addInitCallback(this::onInit, this);
}
private void onInit() {
mShellController.addKeyguardChangeListener(this);
mDisplayController.addDisplayWindowListener(this);
mImeController.addPositionProcessor(this);
+ mCompatUIShellCommandHandler.onInit();
}
/** Sets the callback for UI interactions. */
@@ -164,6 +186,9 @@
*/
public void onCompatInfoChanged(TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
+ if (taskInfo != null && !taskInfo.topActivityInSizeCompat) {
+ mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
+ }
if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
removeLayouts(taskInfo.taskId);
@@ -172,6 +197,7 @@
createOrUpdateCompatLayout(taskInfo, taskListener);
createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
+ createOrUpdateRestartDialogLayout(taskInfo, taskListener);
}
@Override
@@ -278,7 +304,21 @@
ShellTaskOrganizer.TaskListener taskListener) {
return new CompatUIWindowManager(context,
taskInfo, mSyncQueue, mCallback, taskListener,
- mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState);
+ mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState,
+ mCompatUIConfiguration, this::onRestartButtonClicked);
+ }
+
+ private void onRestartButtonClicked(
+ Pair<TaskInfo, ShellTaskOrganizer.TaskListener> taskInfoState) {
+ if (mCompatUIConfiguration.isRestartDialogEnabled()
+ && !mCompatUIConfiguration.getDontShowRestartDialogAgain(
+ taskInfoState.first)) {
+ // We need to show the dialog
+ mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId);
+ onCompatInfoChanged(taskInfoState.first, taskInfoState.second);
+ } else {
+ mCallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId);
+ }
}
private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
@@ -327,6 +367,60 @@
mActiveLetterboxEduLayout = null;
}
+ private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ RestartDialogWindowManager layout =
+ mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
+ if (layout != null) {
+ // TODO(b/266262111) Handle theme change when taskListener changes
+ if (layout.getTaskListener() != taskListener) {
+ mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
+ }
+ layout.setRequestRestartDialog(
+ mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
+ // UI already exists, update the UI layout.
+ if (!layout.updateCompatInfo(taskInfo, taskListener,
+ showOnDisplay(layout.getDisplayId()))) {
+ // The layout is no longer eligible to be shown, remove from active layouts.
+ mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId);
+ }
+ return;
+ }
+ // Create a new UI layout.
+ final Context context = getOrCreateDisplayContext(taskInfo.displayId);
+ if (context == null) {
+ return;
+ }
+ layout = createRestartDialogWindowManager(context, taskInfo, taskListener);
+ layout.setRequestRestartDialog(
+ mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
+ if (layout.createLayout(showOnDisplay(taskInfo.displayId))) {
+ // The new layout is eligible to be shown, add it the active layouts.
+ mTaskIdToRestartDialogWindowManagerMap.put(taskInfo.taskId, layout);
+ }
+ }
+
+ @VisibleForTesting
+ RestartDialogWindowManager createRestartDialogWindowManager(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ return new RestartDialogWindowManager(context, taskInfo, mSyncQueue, taskListener,
+ mDisplayController.getDisplayLayout(taskInfo.displayId), mTransitionsLazy.get(),
+ this::onRestartDialogCallback, this::onRestartDialogDismissCallback,
+ mCompatUIConfiguration);
+ }
+
+ private void onRestartDialogCallback(
+ Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
+ mTaskIdToRestartDialogWindowManagerMap.remove(stateInfo.first.taskId);
+ mCallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId);
+ }
+
+ private void onRestartDialogDismissCallback(
+ Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
+ mSetOfTaskIdsShowingRestartDialog.remove(stateInfo.first.taskId);
+ onCompatInfoChanged(stateInfo.first, stateInfo.second);
+ }
+
private void removeLayouts(int taskId) {
final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId);
if (layout != null) {
@@ -338,6 +432,14 @@
mActiveLetterboxEduLayout.release();
mActiveLetterboxEduLayout = null;
}
+
+ final RestartDialogWindowManager restartLayout =
+ mTaskIdToRestartDialogWindowManagerMap.get(taskId);
+ if (restartLayout != null) {
+ restartLayout.release();
+ mTaskIdToRestartDialogWindowManagerMap.remove(taskId);
+ mSetOfTaskIdsShowingRestartDialog.remove(taskId);
+ }
}
private Context getOrCreateDisplayContext(int displayId) {
@@ -382,6 +484,14 @@
if (mActiveLetterboxEduLayout != null && condition.test(mActiveLetterboxEduLayout)) {
callback.accept(mActiveLetterboxEduLayout);
}
+ for (int i = 0; i < mTaskIdToRestartDialogWindowManagerMap.size(); i++) {
+ final int taskId = mTaskIdToRestartDialogWindowManagerMap.keyAt(i);
+ final RestartDialogWindowManager layout =
+ mTaskIdToRestartDialogWindowManagerMap.get(taskId);
+ if (layout != null && condition.test(layout)) {
+ callback.accept(layout);
+ }
+ }
}
/** An implementation of {@link OnInsetsChangedListener} for a given display id. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index bce3ec4..fe95d04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -21,12 +21,14 @@
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,6 +40,8 @@
import com.android.wm.shell.compatui.CompatUIController.CompatUICallback;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
+import java.util.function.Consumer;
+
/**
* Window manager for the Size Compat restart button and Camera Compat control.
*/
@@ -50,6 +54,13 @@
private final CompatUICallback mCallback;
+ private final CompatUIConfiguration mCompatUIConfiguration;
+
+ private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
+
+ @NonNull
+ private TaskInfo mTaskInfo;
+
// Remember the last reported states in case visibility changes due to keyguard or IME updates.
@VisibleForTesting
boolean mHasSizeCompat;
@@ -68,12 +79,16 @@
CompatUIWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, CompatUICallback callback,
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
- CompatUIHintsState compatUIHintsState) {
+ CompatUIHintsState compatUIHintsState, CompatUIConfiguration compatUIConfiguration,
+ Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
+ mTaskInfo = taskInfo;
mCallback = callback;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
mCameraCompatControlState = taskInfo.cameraCompatControlState;
mCompatUIHintsState = compatUIHintsState;
+ mCompatUIConfiguration = compatUIConfiguration;
+ mOnRestartButtonClicked = onRestartButtonClicked;
}
@Override
@@ -119,6 +134,7 @@
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
+ mTaskInfo = taskInfo;
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
@@ -138,7 +154,7 @@
/** Called when the restart button is clicked. */
void onRestartButtonClicked() {
- mCallback.onSizeCompatRestartButtonClicked(mTaskId);
+ mOnRestartButtonClicked.accept(Pair.create(mTaskInfo, getTaskListener()));
}
/** Called when the camera treatment button is clicked. */
@@ -199,8 +215,14 @@
: taskStableBounds.right - taskBounds.left - mLayout.getMeasuredWidth();
final int positionY = taskStableBounds.bottom - taskBounds.top
- mLayout.getMeasuredHeight();
-
+ // To secure a proper visualisation, we hide the layout while updating the position of
+ // the {@link SurfaceControl} it belongs.
+ final int oldVisibility = mLayout.getVisibility();
+ if (oldVisibility == View.VISIBLE) {
+ mLayout.setVisibility(View.GONE);
+ }
updateSurfacePosition(positionX, positionY);
+ mLayout.setVisibility(oldVisibility);
}
private void updateVisibilityOfViews() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index 2cc9f45..34e650a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -151,6 +151,7 @@
@Override
public void setConfiguration(Configuration configuration) {
super.setConfiguration(configuration);
+ // TODO(b/266262111): Investigate loss of theme configuration when switching TaskListener
mContext = mContext.createConfigurationContext(configuration);
}
@@ -168,6 +169,10 @@
return mLeash;
}
+ protected ShellTaskOrganizer.TaskListener getTaskListener() {
+ return mTaskListener;
+ }
+
/** Inits the z-order of the surface. */
private void initSurface(SurfaceControl leash) {
final int z = getZOrder();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
new file mode 100644
index 0000000..c53e638
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.wm.shell.R;
+
+import java.util.function.Consumer;
+
+/**
+ * Container for a SCM restart confirmation dialog and background dim.
+ */
+public class RestartDialogLayout extends ConstraintLayout implements DialogContainerSupplier {
+
+ private View mDialogContainer;
+ private TextView mDialogTitle;
+ private Drawable mBackgroundDim;
+
+ public RestartDialogLayout(Context context) {
+ this(context, null);
+ }
+
+ public RestartDialogLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public View getDialogContainerView() {
+ return mDialogContainer;
+ }
+
+ TextView getDialogTitle() {
+ return mDialogTitle;
+ }
+
+ @Override
+ public Drawable getBackgroundDimDrawable() {
+ return mBackgroundDim;
+ }
+
+ /**
+ * Register a callback for the dismiss button and background dim.
+ *
+ * @param callback The callback to register or null if all on click listeners should be removed.
+ */
+ void setDismissOnClickListener(@Nullable Runnable callback) {
+ final OnClickListener listener = callback == null ? null : view -> callback.run();
+ findViewById(R.id.letterbox_restart_dialog_dismiss_button).setOnClickListener(listener);
+ }
+
+ /**
+ * Register a callback for the restart button
+ *
+ * @param callback The callback to register or null if all on click listeners should be removed.
+ */
+ void setRestartOnClickListener(@Nullable Consumer<Boolean> callback) {
+ final CheckBox dontShowAgainCheckbox = findViewById(R.id.letterbox_restart_dialog_checkbox);
+ final OnClickListener listener = callback == null ? null : view -> callback.accept(
+ dontShowAgainCheckbox.isChecked());
+ findViewById(R.id.letterbox_restart_dialog_restart_button).setOnClickListener(listener);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mDialogContainer = findViewById(R.id.letterbox_restart_dialog_container);
+ mDialogTitle = findViewById(R.id.letterbox_restart_dialog_title);
+ mBackgroundDim = getBackground().mutate();
+ // Set the alpha of the background dim to 0 for enter animation.
+ mBackgroundDim.setAlpha(0);
+ // We add a no-op on-click listener to the dialog container so that clicks on it won't
+ // propagate to the listener of the layout (which represents the background dim).
+ mDialogContainer.setOnClickListener(view -> {});
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
new file mode 100644
index 0000000..10f25d0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.provider.Settings;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.function.Consumer;
+
+/**
+ * Window manager for the Restart Dialog.
+ *
+ * TODO(b/263484314): Create abstraction of RestartDialogWindowManager and LetterboxEduWindowManager
+ */
+class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
+
+ /**
+ * The restart dialog should be the topmost child of the Task in case there can be more
+ * than one child.
+ */
+ private static final int Z_ORDER = Integer.MAX_VALUE;
+
+ private final DialogAnimationController<RestartDialogLayout> mAnimationController;
+
+ private final Transitions mTransitions;
+
+ // Remember the last reported state in case visibility changes due to keyguard or IME updates.
+ private boolean mRequestRestartDialog;
+
+ private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback;
+
+ private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartCallback;
+
+ private final CompatUIConfiguration mCompatUIConfiguration;
+
+ /**
+ * The vertical margin between the dialog container and the task stable bounds (excluding
+ * insets).
+ */
+ private final int mDialogVerticalMargin;
+
+ @NonNull
+ private TaskInfo mTaskInfo;
+
+ @Nullable
+ @VisibleForTesting
+ RestartDialogLayout mLayout;
+
+ RestartDialogWindowManager(Context context, TaskInfo taskInfo,
+ SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+ DisplayLayout displayLayout, Transitions transitions,
+ Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
+ Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
+ CompatUIConfiguration compatUIConfiguration) {
+ this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions,
+ onRestartCallback, onDismissCallback,
+ new DialogAnimationController<>(context, "RestartDialogWindowManager"),
+ compatUIConfiguration);
+ }
+
+ @VisibleForTesting
+ RestartDialogWindowManager(Context context, TaskInfo taskInfo,
+ SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+ DisplayLayout displayLayout, Transitions transitions,
+ Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
+ Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
+ DialogAnimationController<RestartDialogLayout> animationController,
+ CompatUIConfiguration compatUIConfiguration) {
+ super(context, taskInfo, syncQueue, taskListener, displayLayout);
+ mTaskInfo = taskInfo;
+ mTransitions = transitions;
+ mOnDismissCallback = onDismissCallback;
+ mOnRestartCallback = onRestartCallback;
+ mAnimationController = animationController;
+ mDialogVerticalMargin = (int) mContext.getResources().getDimension(
+ R.dimen.letterbox_restart_dialog_margin);
+ mCompatUIConfiguration = compatUIConfiguration;
+ }
+
+ @Override
+ protected int getZOrder() {
+ return Z_ORDER;
+ }
+
+ @Override
+ @Nullable
+ protected View getLayout() {
+ return mLayout;
+ }
+
+ @Override
+ protected void removeLayout() {
+ mLayout = null;
+ }
+
+ @Override
+ protected boolean eligibleToShowLayout() {
+ // We don't show this dialog if the user has explicitly selected so clicking on a checkbox.
+ return mRequestRestartDialog && !isTaskbarEduShowing() && (mLayout != null
+ || !mCompatUIConfiguration.getDontShowRestartDialogAgain(mTaskInfo));
+ }
+
+ @Override
+ protected View createLayout() {
+ mLayout = inflateLayout();
+ updateDialogMargins();
+
+ // startEnterAnimation will be called immediately if shell-transitions are disabled.
+ mTransitions.runOnIdle(this::startEnterAnimation);
+
+ return mLayout;
+ }
+
+ void setRequestRestartDialog(boolean enabled) {
+ mRequestRestartDialog = enabled;
+ }
+
+ @Override
+ public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
+ boolean canShow) {
+ mTaskInfo = taskInfo;
+ return super.updateCompatInfo(taskInfo, taskListener, canShow);
+ }
+
+ private void updateDialogMargins() {
+ if (mLayout == null) {
+ return;
+ }
+ final View dialogContainer = mLayout.getDialogContainerView();
+ ViewGroup.MarginLayoutParams marginParams =
+ (ViewGroup.MarginLayoutParams) dialogContainer.getLayoutParams();
+
+ final Rect taskBounds = getTaskBounds();
+ final Rect taskStableBounds = getTaskStableBounds();
+
+ marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
+ marginParams.bottomMargin =
+ taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
+ dialogContainer.setLayoutParams(marginParams);
+ }
+
+ private RestartDialogLayout inflateLayout() {
+ return (RestartDialogLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.letterbox_restart_dialog_layout, null);
+ }
+
+ private void startEnterAnimation() {
+ if (mLayout == null) {
+ // Dialog has already been released.
+ return;
+ }
+ mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
+ this::onDialogEnterAnimationEnded);
+ }
+
+ private void onDialogEnterAnimationEnded() {
+ if (mLayout == null) {
+ // Dialog has already been released.
+ return;
+ }
+ mLayout.setDismissOnClickListener(this::onDismiss);
+ mLayout.setRestartOnClickListener(dontShowAgain -> {
+ if (mLayout != null) {
+ mLayout.setDismissOnClickListener(null);
+ mAnimationController.startExitAnimation(mLayout, () -> {
+ release();
+ });
+ }
+ if (dontShowAgain) {
+ mCompatUIConfiguration.setDontShowRestartDialogAgain(mTaskInfo);
+ }
+ mOnRestartCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
+ });
+ // Focus on the dialog title for accessibility.
+ mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+
+ private void onDismiss() {
+ if (mLayout == null) {
+ return;
+ }
+
+ mLayout.setDismissOnClickListener(null);
+ mAnimationController.startExitAnimation(mLayout, () -> {
+ release();
+ mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
+ });
+ }
+
+ @Override
+ public void release() {
+ mAnimationController.cancelAnimation();
+ super.release();
+ }
+
+ @Override
+ protected void onParentBoundsChanged() {
+ if (mLayout == null) {
+ return;
+ }
+ // Both the layout dimensions and dialog margins depend on the parent bounds.
+ WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
+ mLayout.setLayoutParams(windowLayoutParams);
+ updateDialogMargins();
+ relayout(windowLayoutParams);
+ }
+
+ @Override
+ protected void updateSurfacePosition() {
+ // Nothing to do, since the position of the surface is fixed to the top left corner (0,0)
+ // of the task (parent surface), which is the default position of a surface.
+ }
+
+ @Override
+ protected WindowManager.LayoutParams getWindowLayoutParams() {
+ final Rect taskBounds = getTaskBounds();
+ return getWindowLayoutParams(/* width= */ taskBounds.width(), /* height= */
+ taskBounds.height());
+ }
+
+ @VisibleForTesting
+ boolean isTaskbarEduShowing() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ LAUNCHER_TASKBAR_EDUCATION_SHOWING, /* def= */ 0) == 1;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index b144d22..be3e0a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -39,6 +39,7 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipBoundsController;
import com.android.wm.shell.pip.tv.TvPipBoundsState;
@@ -52,11 +53,11 @@
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
-import java.util.Optional;
-
import dagger.Module;
import dagger.Provides;
+import java.util.Optional;
+
/**
* Provides TV specific dependencies for Pip.
*/
@@ -69,6 +70,7 @@
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -89,6 +91,7 @@
shellInit,
shellController,
tvPipBoundsState,
+ pipSizeSpecHandler,
tvPipBoundsAlgorithm,
tvPipBoundsController,
pipAppOpsListener,
@@ -129,14 +132,23 @@
@WMSingleton
@Provides
static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
- TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
- return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm);
+ TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
+ PipSizeSpecHandler pipSizeSpecHandler) {
+ return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm,
+ pipSizeSpecHandler);
}
@WMSingleton
@Provides
- static TvPipBoundsState provideTvPipBoundsState(Context context) {
- return new TvPipBoundsState(context);
+ static TvPipBoundsState provideTvPipBoundsState(Context context,
+ PipSizeSpecHandler pipSizeSpecHandler) {
+ return new TvPipBoundsState(context, pipSizeSpecHandler);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
+ return new PipSizeSpecHandler(context);
}
// Handler needed for loadDrawableAsync() in PipControlsViewController
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 7055aca0..eee8ad8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -57,7 +57,9 @@
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
@@ -197,10 +199,11 @@
DisplayController displayController, DisplayInsetsController displayInsetsController,
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy,
- DockStateReader dockStateReader) {
+ DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration,
+ CompatUIShellCommandHandler compatUIShellCommandHandler) {
return new CompatUIController(context, shellInit, shellController, displayController,
displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy,
- dockStateReader);
+ dockStateReader, compatUIConfiguration, compatUIShellCommandHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 512a4ef..4879d86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -77,6 +77,7 @@
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -98,15 +99,15 @@
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
/**
* Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
* accessible from components within the WM subcomponent (can be explicitly exposed to the
@@ -338,6 +339,7 @@
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -354,17 +356,18 @@
return Optional.ofNullable(PipController.create(
context, shellInit, shellCommandHandler, shellController,
displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
- pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
- phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler,
- pipTransitionController, windowManagerShellWrapper, taskStackListener,
- pipParamsChangedForwarder, displayInsetsController, oneHandedController,
- mainExecutor));
+ pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipMotionHelper,
+ pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
+ pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+ taskStackListener, pipParamsChangedForwarder, displayInsetsController,
+ oneHandedController, mainExecutor));
}
@WMSingleton
@Provides
- static PipBoundsState providePipBoundsState(Context context) {
- return new PipBoundsState(context);
+ static PipBoundsState providePipBoundsState(Context context,
+ PipSizeSpecHandler pipSizeSpecHandler) {
+ return new PipBoundsState(context, pipSizeSpecHandler);
}
@WMSingleton
@@ -381,11 +384,18 @@
@WMSingleton
@Provides
+ static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
+ return new PipSizeSpecHandler(context);
+ }
+
+ @WMSingleton
+ @Provides
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
- PhonePipKeepClearAlgorithm pipKeepClearAlgorithm) {
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipSizeSpecHandler pipSizeSpecHandler) {
return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
- pipKeepClearAlgorithm);
+ pipKeepClearAlgorithm, pipSizeSpecHandler);
}
// Handler is used by Icon.loadDrawableAsync
@@ -409,13 +419,14 @@
PhonePipMenuController menuPhoneController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipTaskOrganizer pipTaskOrganizer,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
- pipBoundsState, pipTaskOrganizer, pipMotionHelper,
+ pipBoundsState, pipSizeSpecHandler, pipTaskOrganizer, pipMotionHelper,
floatingContentCoordinator, pipUiEventLogger, mainExecutor);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index f6d67d8..867162b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -16,24 +16,19 @@
package com.android.wm.shell.pip;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.Size;
-import android.util.TypedValue;
import android.view.Gravity;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import java.io.PrintWriter;
@@ -45,33 +40,29 @@
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- private final @NonNull PipBoundsState mPipBoundsState;
+ @NonNull private final PipBoundsState mPipBoundsState;
+ @NonNull protected final PipSizeSpecHandler mPipSizeSpecHandler;
private final PipSnapAlgorithm mSnapAlgorithm;
private final PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
- private float mDefaultSizePercent;
- private float mMinAspectRatioForMinSize;
- private float mMaxAspectRatioForMinSize;
private float mDefaultAspectRatio;
private float mMinAspectRatio;
private float mMaxAspectRatio;
private int mDefaultStackGravity;
- private int mDefaultMinSize;
- private int mOverridableMinSize;
- protected Point mScreenEdgeInsets;
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm,
- @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm) {
+ @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
mPipBoundsState = pipBoundsState;
mSnapAlgorithm = pipSnapAlgorithm;
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
+ mPipSizeSpecHandler = pipSizeSpecHandler;
reloadResources(context);
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
// resources as it would clobber mAspectRatio when entering PiP from fullscreen which
// triggers a configuration change and the resources to be reloaded.
mPipBoundsState.setAspectRatio(mDefaultAspectRatio);
- mPipBoundsState.setMinEdgeSize(mDefaultMinSize);
}
/**
@@ -83,27 +74,15 @@
R.dimen.config_pictureInPictureDefaultAspectRatio);
mDefaultStackGravity = res.getInteger(
R.integer.config_defaultPictureInPictureGravity);
- mDefaultMinSize = res.getDimensionPixelSize(
- R.dimen.default_minimal_size_pip_resizable_task);
- mOverridableMinSize = res.getDimensionPixelSize(
- R.dimen.overridable_minimal_size_pip_resizable_task);
final String screenEdgeInsetsDpString = res.getString(
R.string.config_defaultPictureInPictureScreenEdgeInsets);
final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
? Size.parseSize(screenEdgeInsetsDpString)
: null;
- mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
- : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
- dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
mMinAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
mMaxAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
- mDefaultSizePercent = res.getFloat(
- R.dimen.config_pictureInPictureDefaultSizePercent);
- mMaxAspectRatioForMinSize = res.getFloat(
- R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
- mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
}
/**
@@ -180,8 +159,9 @@
if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
// If either dimension is smaller than the allowed minimum, adjust them
// according to mOverridableMinSize
- return new Size(Math.max(windowLayout.minWidth, mOverridableMinSize),
- Math.max(windowLayout.minHeight, mOverridableMinSize));
+ return new Size(
+ Math.max(windowLayout.minWidth, mPipSizeSpecHandler.getOverrideMinEdgeSize()),
+ Math.max(windowLayout.minHeight, mPipSizeSpecHandler.getOverrideMinEdgeSize()));
}
return null;
}
@@ -243,28 +223,13 @@
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds), mPipBoundsState.getStashedState());
- final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
final Size size;
if (useCurrentMinEdgeSize || useCurrentSize) {
- // The default minimum edge size, or the override min edge size if set.
- final int defaultMinEdgeSize = overrideMinSize == null ? mDefaultMinSize
- : mPipBoundsState.getOverrideMinEdgeSize();
- final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize()
- : defaultMinEdgeSize;
- // Use the existing size but adjusted to the aspect ratio and min edge size.
- size = getSizeForAspectRatio(
- new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
+ // Use the existing size but adjusted to the new aspect ratio.
+ size = mPipSizeSpecHandler.getSizeForAspectRatio(
+ new Size(stackBounds.width(), stackBounds.height()), aspectRatio);
} else {
- if (overrideMinSize != null) {
- // The override minimal size is set, use that as the default size making sure it's
- // adjusted to the aspect ratio.
- size = adjustSizeToAspectRatio(overrideMinSize, aspectRatio);
- } else {
- // Calculate the default size using the display size and default min edge size.
- final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
- size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
- displayLayout.width(), displayLayout.height());
- }
+ size = mPipSizeSpecHandler.getDefaultSize(aspectRatio);
}
final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
@@ -273,18 +238,6 @@
mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
}
- /** Adjusts the given size to conform to the given aspect ratio. */
- private Size adjustSizeToAspectRatio(@NonNull Size size, float aspectRatio) {
- final float sizeAspectRatio = size.getWidth() / (float) size.getHeight();
- if (sizeAspectRatio > aspectRatio) {
- // Size is wider, fix the width and increase the height
- return new Size(size.getWidth(), (int) (size.getWidth() / aspectRatio));
- } else {
- // Size is taller, fix the height and adjust the width.
- return new Size((int) (size.getHeight() * aspectRatio), size.getHeight());
- }
- }
-
/**
* @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
* provided, then it will apply the default bounds to the provided snap fraction and size.
@@ -303,17 +256,9 @@
final Size defaultSize;
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
- final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
- if (overrideMinSize != null) {
- // The override minimal size is set, use that as the default size making sure it's
- // adjusted to the aspect ratio.
- defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio);
- } else {
- // Calculate the default size using the display size and default min edge size.
- defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
- mDefaultMinSize, displayLayout.width(), displayLayout.height());
- }
+
+ // Calculate the default size
+ defaultSize = mPipSizeSpecHandler.getDefaultSize(mDefaultAspectRatio);
// Now that we have the default size, apply the snap fraction if valid or position the
// bounds using the default gravity.
@@ -335,12 +280,7 @@
* Populates the bounds on the screen that the PIP can be visible in.
*/
public void getInsetBounds(Rect outRect) {
- final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
- Rect insets = mPipBoundsState.getDisplayLayout().stableInsets();
- outRect.set(insets.left + mScreenEdgeInsets.x,
- insets.top + mScreenEdgeInsets.y,
- displayLayout.width() - insets.right - mScreenEdgeInsets.x,
- displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+ outRect.set(mPipSizeSpecHandler.getInsetBounds());
}
/**
@@ -405,71 +345,11 @@
mSnapAlgorithm.applySnapFraction(stackBounds, movementBounds, snapFraction);
}
- public int getDefaultMinSize() {
- return mDefaultMinSize;
- }
-
/**
* @return the pixels for a given dp value.
*/
private int dpToPx(float dpValue, DisplayMetrics dm) {
- return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
- }
-
- /**
- * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge
- * is at least minEdgeSize.
- */
- public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
- int displayHeight) {
- final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
- final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
-
- final int width;
- final int height;
- if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
- // Beyond these points, we can just use the min size as the shorter edge
- if (aspectRatio <= 1) {
- // Portrait, width is the minimum size
- width = minSize;
- height = Math.round(width / aspectRatio);
- } else {
- // Landscape, height is the minimum size
- height = minSize;
- width = Math.round(height * aspectRatio);
- }
- } else {
- // Within these points, we ensure that the bounds fit within the radius of the limits
- // at the points
- final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
- final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
- height = (int) Math.round(Math.sqrt((radius * radius)
- / (aspectRatio * aspectRatio + 1)));
- width = Math.round(height * aspectRatio);
- }
- return new Size(width, height);
- }
-
- /**
- * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
- * minimum edge is at least minEdgeSize.
- */
- public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
- final int smallestSize = Math.min(size.getWidth(), size.getHeight());
- final int minSize = (int) Math.max(minEdgeSize, smallestSize);
-
- final int width;
- final int height;
- if (aspectRatio <= 1) {
- // Portrait, width is the minimum size.
- width = minSize;
- height = Math.round(width / aspectRatio);
- } else {
- // Landscape, height is the minimum size
- height = minSize;
- width = Math.round(height * aspectRatio);
- }
- return new Size(width, height);
+ return PipUtils.dpToPx(dpValue, dm);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 5376ae3..61da10b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -37,6 +37,7 @@
import com.android.internal.util.function.TriConsumer;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -83,13 +84,10 @@
private int mStashedState = STASH_TYPE_NONE;
private int mStashOffset;
private @Nullable PipReentryState mPipReentryState;
+ private final @Nullable PipSizeSpecHandler mPipSizeSpecHandler;
private @Nullable ComponentName mLastPipComponentName;
private int mDisplayId = Display.DEFAULT_DISPLAY;
private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
- /** The current minimum edge size of PIP. */
- private int mMinEdgeSize;
- /** The preferred minimum (and default) size specified by apps. */
- private @Nullable Size mOverrideMinSize;
private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
private boolean mIsImeShowing;
private int mImeHeight;
@@ -122,9 +120,10 @@
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
- public PipBoundsState(@NonNull Context context) {
+ public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler) {
mContext = context;
reloadResources();
+ mPipSizeSpecHandler = pipSizeSpecHandler;
}
/** Reloads the resources. */
@@ -323,20 +322,10 @@
mPipReentryState = null;
}
- /** Set the PIP minimum edge size. */
- public void setMinEdgeSize(int minEdgeSize) {
- mMinEdgeSize = minEdgeSize;
- }
-
- /** Returns the PIP's current minimum edge size. */
- public int getMinEdgeSize() {
- return mMinEdgeSize;
- }
-
/** Sets the preferred size of PIP as specified by the activity in PIP mode. */
public void setOverrideMinSize(@Nullable Size overrideMinSize) {
- final boolean changed = !Objects.equals(overrideMinSize, mOverrideMinSize);
- mOverrideMinSize = overrideMinSize;
+ final boolean changed = !Objects.equals(overrideMinSize, getOverrideMinSize());
+ mPipSizeSpecHandler.setOverrideMinSize(overrideMinSize);
if (changed && mOnMinimalSizeChangeCallback != null) {
mOnMinimalSizeChangeCallback.run();
}
@@ -345,13 +334,12 @@
/** Returns the preferred minimal size specified by the activity in PIP. */
@Nullable
public Size getOverrideMinSize() {
- return mOverrideMinSize;
+ return mPipSizeSpecHandler.getOverrideMinSize();
}
/** Returns the minimum edge size of the override minimum size, or 0 if not set. */
public int getOverrideMinEdgeSize() {
- if (mOverrideMinSize == null) return 0;
- return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight());
+ return mPipSizeSpecHandler.getOverrideMinEdgeSize();
}
/** Get the state of the bounds in motion. */
@@ -581,11 +569,8 @@
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
- pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
pw.println(innerPrefix + "mStashedState=" + mStashedState);
pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
- pw.println(innerPrefix + "mMinEdgeSize=" + mMinEdgeSize);
- pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index fa00619..8b98790 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
@@ -26,8 +27,10 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
+import android.util.TypedValue;
import android.window.TaskSnapshot;
import com.android.internal.protolog.common.ProtoLog;
@@ -70,6 +73,13 @@
}
/**
+ * @return the pixels for a given dp value.
+ */
+ public static int dpToPx(float dpValue, DisplayMetrics dm) {
+ return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
+ }
+
+ /**
* @return true if the aspect ratios differ
*/
public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 525beb1..d86468a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -137,6 +137,7 @@
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
private PipBoundsState mPipBoundsState;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
private PipMotionHelper mPipMotionHelper;
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
@@ -380,6 +381,7 @@
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -401,11 +403,11 @@
return new PipController(context, shellInit, shellCommandHandler, shellController,
displayController, pipAnimationController, pipAppOpsListener,
- pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
- pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
- taskStackListener, pipParamsChangedForwarder, displayInsetsController,
- oneHandedController, mainExecutor)
+ pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler,
+ pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+ pipTransitionState, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+ displayInsetsController, oneHandedController, mainExecutor)
.mImpl;
}
@@ -419,6 +421,7 @@
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
@NonNull PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -444,6 +447,7 @@
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipSizeSpecHandler = pipSizeSpecHandler;
mPipMotionHelper = pipMotionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTransitionState = pipTransitionState;
@@ -512,7 +516,10 @@
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
mPipBoundsState.setDisplayId(mContext.getDisplayId());
- mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
+
+ DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
+ mPipSizeSpecHandler.setDisplayLayout(layout);
+ mPipBoundsState.setDisplayLayout(layout);
try {
mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener);
@@ -686,6 +693,7 @@
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
mPipBoundsState.onConfigurationChanged();
+ mPipSizeSpecHandler.onConfigurationChanged();
}
@Override
@@ -711,7 +719,11 @@
Runnable updateDisplayLayout = () -> {
final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
&& mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
+
+ // update the internal state of objects subscribed to display changes
+ mPipSizeSpecHandler.setDisplayLayout(layout);
mPipBoundsState.setDisplayLayout(layout);
+
final WindowContainerTransaction wct =
fromRotation ? new WindowContainerTransaction() : null;
updateMovementBounds(null /* toBounds */,
@@ -1083,6 +1095,7 @@
mPipTaskOrganizer.dump(pw, innerPrefix);
mPipBoundsState.dump(pw, innerPrefix);
mPipInputConsumer.dump(pw, innerPrefix);
+ mPipSizeSpecHandler.dump(pw, innerPrefix);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
new file mode 100644
index 0000000..a906522
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import static com.android.wm.shell.pip.PipUtils.dpToPx;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.SystemProperties;
+import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.io.PrintWriter;
+
+/**
+ * Acts as a source of truth for appropriate size spec for PIP.
+ */
+public class PipSizeSpecHandler {
+ private static final String TAG = PipSizeSpecHandler.class.getSimpleName();
+
+ @NonNull private final DisplayLayout mDisplayLayout = new DisplayLayout();
+
+ @VisibleForTesting
+ final SizeSpecSource mSizeSpecSourceImpl;
+
+ /** The preferred minimum (and default) size specified by apps. */
+ @Nullable private Size mOverrideMinSize;
+
+ /** Used to store values obtained from resource files. */
+ private Point mScreenEdgeInsets;
+ private float mMinAspectRatioForMinSize;
+ private float mMaxAspectRatioForMinSize;
+ private int mDefaultMinSize;
+
+ @NonNull private final Context mContext;
+
+ private interface SizeSpecSource {
+ /** Returns max size allowed for the PIP window */
+ Size getMaxSize(float aspectRatio);
+
+ /** Returns default size for the PIP window */
+ Size getDefaultSize(float aspectRatio);
+
+ /** Returns min size allowed for the PIP window */
+ Size getMinSize(float aspectRatio);
+
+ /** Returns the adjusted size based on current size and target aspect ratio */
+ Size getSizeForAspectRatio(Size size, float aspectRatio);
+
+ /** Updates internal resources on configuration changes */
+ default void reloadResources() {}
+ }
+
+ /**
+ * Determines PIP window size optimized for large screens and aspect ratios close to 1:1
+ */
+ private class SizeSpecLargeScreenOptimizedImpl implements SizeSpecSource {
+ private static final float DEFAULT_OPTIMIZED_ASPECT_RATIO = 9f / 16;
+
+ /** Default and minimum percentages for the PIP size logic. */
+ private final float mDefaultSizePercent;
+ private final float mMinimumSizePercent;
+
+ /** Aspect ratio that the PIP size spec logic optimizes for. */
+ private float mOptimizedAspectRatio;
+
+ private SizeSpecLargeScreenOptimizedImpl() {
+ mDefaultSizePercent = Float.parseFloat(SystemProperties
+ .get("com.android.wm.shell.pip.phone.def_percentage", "0.6"));
+ mMinimumSizePercent = Float.parseFloat(SystemProperties
+ .get("com.android.wm.shell.pip.phone.min_percentage", "0.5"));
+ }
+
+ @Override
+ public void reloadResources() {
+ final Resources res = mContext.getResources();
+
+ mOptimizedAspectRatio = res.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio);
+ // make sure the optimized aspect ratio is valid with a default value to fall back to
+ if (mOptimizedAspectRatio > 1) {
+ mOptimizedAspectRatio = DEFAULT_OPTIMIZED_ASPECT_RATIO;
+ }
+ }
+
+ /**
+ * Calculates the max size of PIP.
+ *
+ * Optimizes for 16:9 aspect ratios, making them take full length of shortest display edge.
+ * As aspect ratio approaches values close to 1:1, the logic does not let PIP occupy the
+ * whole screen. A linear function is used to calculate these sizes.
+ *
+ * @param aspectRatio aspect ratio of the PIP window
+ * @return dimensions of the max size of the PIP
+ */
+ @Override
+ public Size getMaxSize(float aspectRatio) {
+ final int totalHorizontalPadding = getInsetBounds().left
+ + (getDisplayBounds().width() - getInsetBounds().right);
+ final int totalVerticalPadding = getInsetBounds().top
+ + (getDisplayBounds().height() - getInsetBounds().bottom);
+
+ final int shorterLength = (int) (1f * Math.min(
+ getDisplayBounds().width() - totalHorizontalPadding,
+ getDisplayBounds().height() - totalVerticalPadding));
+
+ int maxWidth, maxHeight;
+
+ // use the optimized max sizing logic only within a certain aspect ratio range
+ if (aspectRatio >= mOptimizedAspectRatio && aspectRatio <= 1 / mOptimizedAspectRatio) {
+ // this formula and its derivation is explained in b/198643358#comment16
+ maxWidth = (int) (mOptimizedAspectRatio * shorterLength
+ + shorterLength * (aspectRatio - mOptimizedAspectRatio) / (1
+ + aspectRatio));
+ maxHeight = (int) (maxWidth / aspectRatio);
+ } else {
+ if (aspectRatio > 1f) {
+ maxWidth = shorterLength;
+ maxHeight = (int) (maxWidth / aspectRatio);
+ } else {
+ maxHeight = shorterLength;
+ maxWidth = (int) (maxHeight * aspectRatio);
+ }
+ }
+
+ return new Size(maxWidth, maxHeight);
+ }
+
+ /**
+ * Decreases the dimensions by a percentage relative to max size to get default size.
+ *
+ * @param aspectRatio aspect ratio of the PIP window
+ * @return dimensions of the default size of the PIP
+ */
+ @Override
+ public Size getDefaultSize(float aspectRatio) {
+ Size minSize = this.getMinSize(aspectRatio);
+
+ if (mOverrideMinSize != null) {
+ return minSize;
+ }
+
+ Size maxSize = this.getMaxSize(aspectRatio);
+
+ int defaultWidth = Math.max((int) (maxSize.getWidth() * mDefaultSizePercent),
+ minSize.getWidth());
+ int defaultHeight = Math.max((int) (maxSize.getHeight() * mDefaultSizePercent),
+ minSize.getHeight());
+
+ return new Size(defaultWidth, defaultHeight);
+ }
+
+ /**
+ * Decreases the dimensions by a certain percentage relative to max size to get min size.
+ *
+ * @param aspectRatio aspect ratio of the PIP window
+ * @return dimensions of the min size of the PIP
+ */
+ @Override
+ public Size getMinSize(float aspectRatio) {
+ // if there is an overridden min size provided, return that
+ if (mOverrideMinSize != null) {
+ return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+ }
+
+ Size maxSize = this.getMaxSize(aspectRatio);
+
+ int minWidth = (int) (maxSize.getWidth() * mMinimumSizePercent);
+ int minHeight = (int) (maxSize.getHeight() * mMinimumSizePercent);
+
+ // make sure the calculated min size is not smaller than the allowed default min size
+ if (aspectRatio > 1f) {
+ minHeight = (int) Math.max(minHeight, mDefaultMinSize);
+ minWidth = (int) (minHeight * aspectRatio);
+ } else {
+ minWidth = (int) Math.max(minWidth, mDefaultMinSize);
+ minHeight = (int) (minWidth / aspectRatio);
+ }
+ return new Size(minWidth, minHeight);
+ }
+
+ /**
+ * Returns the size for target aspect ratio making sure new size conforms with the rules.
+ *
+ * <p>Recalculates the dimensions such that the target aspect ratio is achieved, while
+ * maintaining the same maximum size to current size ratio.
+ *
+ * @param size current size
+ * @param aspectRatio target aspect ratio
+ */
+ @Override
+ public Size getSizeForAspectRatio(Size size, float aspectRatio) {
+ // getting the percentage of the max size that current size takes
+ float currAspectRatio = (float) size.getWidth() / size.getHeight();
+ Size currentMaxSize = getMaxSize(currAspectRatio);
+ float currentPercent = (float) size.getWidth() / currentMaxSize.getWidth();
+
+ // getting the max size for the target aspect ratio
+ Size updatedMaxSize = getMaxSize(aspectRatio);
+
+ int width = (int) (updatedMaxSize.getWidth() * currentPercent);
+ int height = (int) (updatedMaxSize.getHeight() * currentPercent);
+
+ // adjust the dimensions if below allowed min edge size
+ if (width < getMinEdgeSize() && aspectRatio <= 1) {
+ width = getMinEdgeSize();
+ height = (int) (width / aspectRatio);
+ } else if (height < getMinEdgeSize() && aspectRatio > 1) {
+ height = getMinEdgeSize();
+ width = (int) (height * aspectRatio);
+ }
+
+ // reduce the dimensions of the updated size to the calculated percentage
+ return new Size(width, height);
+ }
+ }
+
+ private class SizeSpecDefaultImpl implements SizeSpecSource {
+ private float mDefaultSizePercent;
+ private float mMinimumSizePercent;
+
+ @Override
+ public void reloadResources() {
+ final Resources res = mContext.getResources();
+
+ mMaxAspectRatioForMinSize = res.getFloat(
+ R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+ mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
+
+ mDefaultSizePercent = res.getFloat(R.dimen.config_pictureInPictureDefaultSizePercent);
+ mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1);
+ }
+
+ @Override
+ public Size getMaxSize(float aspectRatio) {
+ final int shorterLength = Math.min(getDisplayBounds().width(),
+ getDisplayBounds().height());
+
+ final int totalHorizontalPadding = getInsetBounds().left
+ + (getDisplayBounds().width() - getInsetBounds().right);
+ final int totalVerticalPadding = getInsetBounds().top
+ + (getDisplayBounds().height() - getInsetBounds().bottom);
+
+ final int maxWidth, maxHeight;
+
+ if (aspectRatio > 1f) {
+ maxWidth = (int) Math.max(getDefaultSize(aspectRatio).getWidth(),
+ shorterLength - totalHorizontalPadding);
+ maxHeight = (int) (maxWidth / aspectRatio);
+ } else {
+ maxHeight = (int) Math.max(getDefaultSize(aspectRatio).getHeight(),
+ shorterLength - totalVerticalPadding);
+ maxWidth = (int) (maxHeight * aspectRatio);
+ }
+
+ return new Size(maxWidth, maxHeight);
+ }
+
+ @Override
+ public Size getDefaultSize(float aspectRatio) {
+ if (mOverrideMinSize != null) {
+ return this.getMinSize(aspectRatio);
+ }
+
+ final int smallestDisplaySize = Math.min(getDisplayBounds().width(),
+ getDisplayBounds().height());
+ final int minSize = (int) Math.max(getMinEdgeSize(),
+ smallestDisplaySize * mDefaultSizePercent);
+
+ final int width;
+ final int height;
+
+ if (aspectRatio <= mMinAspectRatioForMinSize
+ || aspectRatio > mMaxAspectRatioForMinSize) {
+ // Beyond these points, we can just use the min size as the shorter edge
+ if (aspectRatio <= 1) {
+ // Portrait, width is the minimum size
+ width = minSize;
+ height = Math.round(width / aspectRatio);
+ } else {
+ // Landscape, height is the minimum size
+ height = minSize;
+ width = Math.round(height * aspectRatio);
+ }
+ } else {
+ // Within these points, ensure that the bounds fit within the radius of the limits
+ // at the points
+ final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
+ final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
+ height = (int) Math.round(Math.sqrt((radius * radius)
+ / (aspectRatio * aspectRatio + 1)));
+ width = Math.round(height * aspectRatio);
+ }
+
+ return new Size(width, height);
+ }
+
+ @Override
+ public Size getMinSize(float aspectRatio) {
+ if (mOverrideMinSize != null) {
+ return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+ }
+
+ final int shorterLength = Math.min(getDisplayBounds().width(),
+ getDisplayBounds().height());
+ final int minWidth, minHeight;
+
+ if (aspectRatio > 1f) {
+ minWidth = (int) Math.min(getDefaultSize(aspectRatio).getWidth(),
+ shorterLength * mMinimumSizePercent);
+ minHeight = (int) (minWidth / aspectRatio);
+ } else {
+ minHeight = (int) Math.min(getDefaultSize(aspectRatio).getHeight(),
+ shorterLength * mMinimumSizePercent);
+ minWidth = (int) (minHeight * aspectRatio);
+ }
+
+ return new Size(minWidth, minHeight);
+ }
+
+ @Override
+ public Size getSizeForAspectRatio(Size size, float aspectRatio) {
+ final int smallestSize = Math.min(size.getWidth(), size.getHeight());
+ final int minSize = Math.max(getMinEdgeSize(), smallestSize);
+
+ final int width;
+ final int height;
+ if (aspectRatio <= 1) {
+ // Portrait, width is the minimum size.
+ width = minSize;
+ height = Math.round(width / aspectRatio);
+ } else {
+ // Landscape, height is the minimum size
+ height = minSize;
+ width = Math.round(height * aspectRatio);
+ }
+
+ return new Size(width, height);
+ }
+ }
+
+ public PipSizeSpecHandler(Context context) {
+ mContext = context;
+
+ boolean enablePipSizeLargeScreen = SystemProperties
+ .getBoolean("persist.wm.debug.enable_pip_size_large_screen", false);
+
+ // choose between two implementations of size spec logic
+ if (enablePipSizeLargeScreen) {
+ mSizeSpecSourceImpl = new SizeSpecLargeScreenOptimizedImpl();
+ } else {
+ mSizeSpecSourceImpl = new SizeSpecDefaultImpl();
+ }
+
+ reloadResources();
+ }
+
+ /** Reloads the resources */
+ public void onConfigurationChanged() {
+ reloadResources();
+ }
+
+ private void reloadResources() {
+ final Resources res = mContext.getResources();
+
+ mDefaultMinSize = res.getDimensionPixelSize(
+ R.dimen.default_minimal_size_pip_resizable_task);
+
+ final String screenEdgeInsetsDpString = res.getString(
+ R.string.config_defaultPictureInPictureScreenEdgeInsets);
+ final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
+ ? Size.parseSize(screenEdgeInsetsDpString)
+ : null;
+ mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
+ : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
+ dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
+
+ // update the internal resources of the size spec source's stub
+ mSizeSpecSourceImpl.reloadResources();
+ }
+
+ /** Returns the display's bounds. */
+ @NonNull
+ public Rect getDisplayBounds() {
+ return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ }
+
+ /** Get the display layout. */
+ @NonNull
+ public DisplayLayout getDisplayLayout() {
+ return mDisplayLayout;
+ }
+
+ /** Update the display layout. */
+ public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
+ mDisplayLayout.set(displayLayout);
+ }
+
+ public Point getScreenEdgeInsets() {
+ return mScreenEdgeInsets;
+ }
+
+ /**
+ * Returns the inset bounds the PIP window can be visible in.
+ */
+ public Rect getInsetBounds() {
+ Rect insetBounds = new Rect();
+ final DisplayLayout displayLayout = getDisplayLayout();
+ Rect insets = getDisplayLayout().stableInsets();
+ insetBounds.set(insets.left + mScreenEdgeInsets.x,
+ insets.top + mScreenEdgeInsets.y,
+ displayLayout.width() - insets.right - mScreenEdgeInsets.x,
+ displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+ return insetBounds;
+ }
+
+ /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
+ public void setOverrideMinSize(@Nullable Size overrideMinSize) {
+ mOverrideMinSize = overrideMinSize;
+ }
+
+ /** Returns the preferred minimal size specified by the activity in PIP. */
+ @Nullable
+ public Size getOverrideMinSize() {
+ return mOverrideMinSize;
+ }
+
+ /** Returns the minimum edge size of the override minimum size, or 0 if not set. */
+ public int getOverrideMinEdgeSize() {
+ if (mOverrideMinSize == null) return 0;
+ return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight());
+ }
+
+ public int getMinEdgeSize() {
+ return mOverrideMinSize == null ? mDefaultMinSize : getOverrideMinEdgeSize();
+ }
+
+ /**
+ * Returns the size for the max size spec.
+ */
+ public Size getMaxSize(float aspectRatio) {
+ return mSizeSpecSourceImpl.getMaxSize(aspectRatio);
+ }
+
+ /**
+ * Returns the size for the default size spec.
+ */
+ public Size getDefaultSize(float aspectRatio) {
+ return mSizeSpecSourceImpl.getDefaultSize(aspectRatio);
+ }
+
+ /**
+ * Returns the size for the min size spec.
+ */
+ public Size getMinSize(float aspectRatio) {
+ return mSizeSpecSourceImpl.getMinSize(aspectRatio);
+ }
+
+ /**
+ * Returns the adjusted size so that it conforms to the given aspectRatio.
+ *
+ * @param size current size
+ * @param aspectRatio target aspect ratio
+ */
+ public Size getSizeForAspectRatio(@NonNull Size size, float aspectRatio) {
+ if (size.equals(mOverrideMinSize)) {
+ return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+ }
+
+ return mSizeSpecSourceImpl.getSizeForAspectRatio(size, aspectRatio);
+ }
+
+ /**
+ * Returns the adjusted overridden min size if it is set; otherwise, returns null.
+ *
+ * <p>Overridden min size needs to be adjusted in its own way while making sure that the target
+ * aspect ratio is maintained
+ *
+ * @param aspectRatio target aspect ratio
+ */
+ @Nullable
+ @VisibleForTesting
+ Size adjustOverrideMinSizeToAspectRatio(float aspectRatio) {
+ if (mOverrideMinSize == null) {
+ return null;
+ }
+ final Size size = mOverrideMinSize;
+ final float sizeAspectRatio = size.getWidth() / (float) size.getHeight();
+ if (sizeAspectRatio > aspectRatio) {
+ // Size is wider, fix the width and increase the height
+ return new Size(size.getWidth(), (int) (size.getWidth() / aspectRatio));
+ } else {
+ // Size is taller, fix the height and adjust the width.
+ return new Size((int) (size.getHeight() * aspectRatio), size.getHeight());
+ }
+ }
+
+ /** Dumps internal state. */
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl.toString());
+ pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
+ pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 850c561..0e8d13d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -78,7 +78,8 @@
private boolean mEnableResize;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
- private final @NonNull PipBoundsState mPipBoundsState;
+ @NonNull private final PipBoundsState mPipBoundsState;
+ @NonNull private final PipSizeSpecHandler mPipSizeSpecHandler;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
private final PipTaskOrganizer mPipTaskOrganizer;
@@ -99,7 +100,6 @@
// The reference inset bounds, used to determine the dismiss fraction
private final Rect mInsetBounds = new Rect();
- private int mExpandedShortestEdgeSize;
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
// us to send stale bounds
@@ -120,7 +120,6 @@
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinDismiss;
- private float mMinimumSizePercent;
// Touch state
private final PipTouchState mTouchState;
@@ -174,6 +173,7 @@
PhonePipMenuController menuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler,
PipTaskOrganizer pipTaskOrganizer,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
@@ -184,6 +184,7 @@
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipSizeSpecHandler = pipSizeSpecHandler;
mPipTaskOrganizer = pipTaskOrganizer;
mMenuController = menuController;
mPipUiEventLogger = pipUiEventLogger;
@@ -271,10 +272,7 @@
private void reloadResources() {
final Resources res = mContext.getResources();
mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
- mExpandedShortestEdgeSize = res.getDimensionPixelSize(
- R.dimen.pip_expanded_shortest_edge_size);
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
- mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1);
mPipDismissTargetHandler.updateMagneticTargetSize();
}
@@ -407,10 +405,7 @@
// Calculate the expanded size
float aspectRatio = (float) normalBounds.width() / normalBounds.height();
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio(
- aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
+ Size expandedSize = mPipSizeSpecHandler.getDefaultSize(aspectRatio);
mPipBoundsState.setExpandedBounds(
new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight()));
Rect expandedMovementBounds = new Rect();
@@ -418,7 +413,7 @@
mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
bottomOffset);
- updatePipSizeConstraints(insetBounds, normalBounds, aspectRatio);
+ updatePipSizeConstraints(normalBounds, aspectRatio);
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
@@ -496,14 +491,14 @@
* @param aspectRatio aspect ratio to use for the calculation of min/max size
*/
public void updateMinMaxSize(float aspectRatio) {
- updatePipSizeConstraints(mInsetBounds, mPipBoundsState.getNormalBounds(),
+ updatePipSizeConstraints(mPipBoundsState.getNormalBounds(),
aspectRatio);
}
- private void updatePipSizeConstraints(Rect insetBounds, Rect normalBounds,
+ private void updatePipSizeConstraints(Rect normalBounds,
float aspectRatio) {
if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
- updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
+ updatePinchResizeSizeConstraints(aspectRatio);
} else {
mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
@@ -511,26 +506,13 @@
}
}
- private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
- float aspectRatio) {
- final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
- mPipBoundsState.getDisplayBounds().height());
- final int totalHorizontalPadding = insetBounds.left
- + (mPipBoundsState.getDisplayBounds().width() - insetBounds.right);
- final int totalVerticalPadding = insetBounds.top
- + (mPipBoundsState.getDisplayBounds().height() - insetBounds.bottom);
+ private void updatePinchResizeSizeConstraints(float aspectRatio) {
final int minWidth, minHeight, maxWidth, maxHeight;
- if (aspectRatio > 1f) {
- minWidth = (int) Math.min(normalBounds.width(), shorterLength * mMinimumSizePercent);
- minHeight = (int) (minWidth / aspectRatio);
- maxWidth = (int) Math.max(normalBounds.width(), shorterLength - totalHorizontalPadding);
- maxHeight = (int) (maxWidth / aspectRatio);
- } else {
- minHeight = (int) Math.min(normalBounds.height(), shorterLength * mMinimumSizePercent);
- minWidth = (int) (minHeight * aspectRatio);
- maxHeight = (int) Math.max(normalBounds.height(), shorterLength - totalVerticalPadding);
- maxWidth = (int) (maxHeight * aspectRatio);
- }
+
+ minWidth = mPipSizeSpecHandler.getMinSize(aspectRatio).getWidth();
+ minHeight = mPipSizeSpecHandler.getMinSize(aspectRatio).getHeight();
+ maxWidth = mPipSizeSpecHandler.getMaxSize(aspectRatio).getWidth();
+ maxHeight = mPipSizeSpecHandler.getMaxSize(aspectRatio).getHeight();
mPipResizeGestureHandler.updateMinSize(minWidth, minHeight);
mPipResizeGestureHandler.updateMaxSize(maxWidth, maxHeight);
@@ -1064,11 +1046,6 @@
mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
mMotionHelper.onMovementBoundsChanged();
-
- boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
- mPipBoundsState.setMinEdgeSize(
- isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize
- : mPipBoundsAlgorithm.getDefaultMinSize());
}
private Rect getMovementBounds(Rect curBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index b042063..825b969 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -39,6 +39,7 @@
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -60,10 +61,10 @@
public TvPipBoundsAlgorithm(Context context,
@NonNull TvPipBoundsState tvPipBoundsState,
- @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
+ @NonNull PipSnapAlgorithm pipSnapAlgorithm,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
super(context, tvPipBoundsState, pipSnapAlgorithm,
- new PipKeepClearAlgorithmInterface() {
- });
+ new PipKeepClearAlgorithmInterface() {}, pipSizeSpecHandler);
this.mTvPipBoundsState = tvPipBoundsState;
this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
reloadResources(context);
@@ -289,7 +290,8 @@
if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
expandedSize = mTvPipBoundsState.getTvExpandedSize();
} else {
- int maxHeight = displayLayout.height() - (2 * mScreenEdgeInsets.y)
+ int maxHeight = displayLayout.height()
+ - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().y)
- pipDecorations.top - pipDecorations.bottom;
float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio;
@@ -308,7 +310,8 @@
if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_VERTICAL) {
expandedSize = mTvPipBoundsState.getTvExpandedSize();
} else {
- int maxWidth = displayLayout.width() - (2 * mScreenEdgeInsets.x)
+ int maxWidth = displayLayout.width()
+ - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().x)
- pipDecorations.left - pipDecorations.right;
float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio;
if (maxWidth > aspectRatioWidth) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index 9c7c0ae..22b3f49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -31,6 +31,7 @@
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -73,8 +74,9 @@
@NonNull
private Insets mPipMenuTemporaryDecorInsets = Insets.NONE;
- public TvPipBoundsState(@NonNull Context context) {
- super(context);
+ public TvPipBoundsState(@NonNull Context context,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
+ super(context, pipSizeSpecHandler);
mContext = context;
updateDefaultGravity();
mPreviousCollapsedGravity = mDefaultGravity;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 4426423..02b9197 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -55,6 +55,7 @@
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellController;
@@ -118,6 +119,7 @@
private final ShellController mShellController;
private final TvPipBoundsState mTvPipBoundsState;
+ private final PipSizeSpecHandler mPipSizeSpecHandler;
private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final TvPipBoundsController mTvPipBoundsController;
private final PipAppOpsListener mAppOpsListener;
@@ -152,6 +154,7 @@
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -171,6 +174,7 @@
shellInit,
shellController,
tvPipBoundsState,
+ pipSizeSpecHandler,
tvPipBoundsAlgorithm,
tvPipBoundsController,
pipAppOpsListener,
@@ -192,6 +196,7 @@
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -212,10 +217,13 @@
mShellController = shellController;
mDisplayController = displayController;
- mTvPipBoundsState = tvPipBoundsState;
- mTvPipBoundsState.setDisplayId(context.getDisplayId());
- mTvPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
+ DisplayLayout layout = new DisplayLayout(context, context.getDisplay());
+ mTvPipBoundsState = tvPipBoundsState;
+ mTvPipBoundsState.setDisplayLayout(layout);
+ mTvPipBoundsState.setDisplayId(context.getDisplayId());
+ mPipSizeSpecHandler = pipSizeSpecHandler;
+ mPipSizeSpecHandler.setDisplayLayout(layout);
mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
mTvPipBoundsController = tvPipBoundsController;
mTvPipBoundsController.setListener(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 38099fc..21eeaa2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -535,17 +536,11 @@
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
- try {
- adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
- ActivityTaskManager.getService().startActivityFromRecents(taskId, options2);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
+ taskId = INVALID_TASK_ID;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
- return;
}
}
mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
@@ -586,17 +581,11 @@
fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
- try {
- adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
- pendingIntent1.send();
- } catch (RemoteException | PendingIntent.CanceledException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
+ pendingIntent2 = null;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
- return;
}
}
mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1, options1,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a6c4ac2..39cf5f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -629,9 +629,19 @@
RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
+ if (pendingIntent2 == null) {
+ // Launching a solo task.
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+ options1 = activityOptions.toBundle();
+ addActivityOptions(options1, null /* launchTarget */);
+ wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+ mSyncQueue.queue(wct);
+ return;
+ }
+
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
-
startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, options2, splitPosition,
splitRatio, adapter, instanceId);
}
@@ -666,9 +676,19 @@
InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
+ if (taskId == INVALID_TASK_ID) {
+ // Launching a solo task.
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+ options1 = activityOptions.toBundle();
+ addActivityOptions(options1, null /* launchTarget */);
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+ mSyncQueue.queue(wct);
+ return;
+ }
+
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
-
startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
instanceId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 129924a..476a7ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -143,8 +143,8 @@
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
|| (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
- && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
- == WINDOWING_MODE_FREEFORM);
+ && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
+ == WINDOWING_MODE_FREEFORM);
}
private void createWindowDecoration(
@@ -174,26 +174,29 @@
final CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.setDragPositioningCallback(taskPositioner);
+ windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
setupCaptionColor(taskInfo, windowDecoration);
}
private class CaptionTouchEventListener implements
- View.OnClickListener, View.OnTouchListener {
+ View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
private final int mTaskId;
private final WindowContainerToken mTaskToken;
- private final DragResizeCallback mDragResizeCallback;
+ private final DragPositioningCallback mDragPositioningCallback;
+ private final DragDetector mDragDetector;
private int mDragPointerId = -1;
private CaptionTouchEventListener(
RunningTaskInfo taskInfo,
- DragResizeCallback dragResizeCallback) {
+ DragPositioningCallback dragPositioningCallback) {
mTaskId = taskInfo.taskId;
mTaskToken = taskInfo.token;
- mDragResizeCallback = dragResizeCallback;
+ mDragPositioningCallback = dragPositioningCallback;
+ mDragDetector = new DragDetector(this);
}
@Override
@@ -216,7 +219,7 @@
if (v.getId() != R.id.caption) {
return false;
}
- handleEventForMove(e);
+ mDragDetector.onMotionEvent(e);
if (e.getAction() != MotionEvent.ACTION_DOWN) {
return false;
@@ -235,32 +238,34 @@
* @param e {@link MotionEvent} to process
* @return {@code true} if a drag is happening; or {@code false} if it is not
*/
- private void handleEventForMove(MotionEvent e) {
+ @Override
+ public boolean handleMotionEvent(MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
- return;
+ return false;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
- mDragResizeCallback.onDragResizeStart(
+ mDragPositioningCallback.onDragPositioningStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
break;
}
case MotionEvent.ACTION_MOVE: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
- mDragResizeCallback.onDragResizeMove(
+ mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
- mDragResizeCallback.onDragResizeEnd(
+ mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
}
+ return true;
}
}
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index d26f1fc..060dc4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -47,9 +47,9 @@
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
- private DragResizeCallback mDragResizeCallback;
+ private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
- private final DragDetector mDragDetector;
+ private DragDetector mDragDetector;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private final RelayoutResult<WindowDecorLinearLayout> mResult =
@@ -69,7 +69,6 @@
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
- mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
}
void setCaptionListeners(
@@ -79,8 +78,13 @@
mOnCaptionTouchListener = onCaptionTouchListener;
}
- void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
- mDragResizeCallback = dragResizeCallback;
+ void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
+ mDragPositioningCallback = dragPositioningCallback;
+ }
+
+ void setDragDetector(DragDetector dragDetector) {
+ mDragDetector = dragDetector;
+ mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
}
@Override
@@ -147,7 +151,7 @@
mChoreographer,
mDisplay.getDisplayId(),
mDecorationContainerSurface,
- mDragResizeCallback);
+ mDragPositioningCallback);
}
final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2863adc..606cf28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -205,34 +205,29 @@
}
private class DesktopModeTouchEventListener implements
- View.OnClickListener, View.OnTouchListener {
+ View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
private final int mTaskId;
private final WindowContainerToken mTaskToken;
- private final DragResizeCallback mDragResizeCallback;
+ private final DragPositioningCallback mDragPositioningCallback;
private final DragDetector mDragDetector;
private int mDragPointerId = -1;
private DesktopModeTouchEventListener(
RunningTaskInfo taskInfo,
- DragResizeCallback dragResizeCallback,
- DragDetector dragDetector) {
+ DragPositioningCallback dragPositioningCallback) {
mTaskId = taskInfo.taskId;
mTaskToken = taskInfo.token;
- mDragResizeCallback = dragResizeCallback;
- mDragDetector = dragDetector;
+ mDragPositioningCallback = dragPositioningCallback;
+ mDragDetector = new DragDetector(this);
}
@Override
public void onClick(View v) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
final int id = v.getId();
- if (id == R.id.close_window) {
- mTaskOperations.closeTask(mTaskToken);
- } else if (id == R.id.back_button) {
- mTaskOperations.injectBackKey();
- } else if (id == R.id.caption_handle) {
+ if (id == R.id.caption_handle) {
decoration.createHandleMenu();
} else if (id == R.id.desktop_button) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
@@ -254,8 +249,7 @@
return false;
}
if (id == R.id.caption_handle) {
- isDrag = mDragDetector.detectDragEvent(e);
- handleEventForMove(e);
+ isDrag = mDragDetector.onMotionEvent(e);
}
if (e.getAction() != MotionEvent.ACTION_DOWN) {
return isDrag;
@@ -272,30 +266,30 @@
/**
* @param e {@link MotionEvent} to process
- * @return {@code true} if a drag is happening; or {@code false} if it is not
+ * @return {@code true} if the motion event is handled.
*/
- private void handleEventForMove(MotionEvent e) {
+ @Override
+ public boolean handleMotionEvent(MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (DesktopModeStatus.isProto2Enabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
- return;
+ return false;
}
if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
- && mDesktopModeController.get().getDisplayAreaWindowingMode(
- taskInfo.displayId)
+ && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
== WINDOWING_MODE_FULLSCREEN) {
- return;
+ return false;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
- mDragResizeCallback.onDragResizeStart(
+ mDragPositioningCallback.onDragPositioningStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
break;
}
case MotionEvent.ACTION_MOVE: {
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
- mDragResizeCallback.onDragResizeMove(
+ mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
@@ -304,7 +298,7 @@
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
final int statusBarHeight = mDisplayController
.getDisplayLayout(taskInfo.displayId).stableInsets().top;
- mDragResizeCallback.onDragResizeEnd(
+ mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
if (e.getRawY(dragPointerIdx) <= statusBarHeight) {
if (DesktopModeStatus.isProto2Enabled()) {
@@ -324,6 +318,7 @@
break;
}
}
+ return true;
}
}
@@ -560,10 +555,10 @@
final TaskPositioner taskPositioner =
new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
final DesktopModeTouchEventListener touchEventListener =
- new DesktopModeTouchEventListener(
- taskInfo, taskPositioner, windowDecoration.getDragDetector());
+ new DesktopModeTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.setDragPositioningCallback(taskPositioner);
+ windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
incrementEventReceiverTasks(taskInfo.displayId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 1a38d24..744c18f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -25,7 +25,6 @@
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
import android.view.Choreographer;
@@ -55,11 +54,12 @@
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
- private DragResizeCallback mDragResizeCallback;
+ private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
- private final DragDetector mDragDetector;
+ private DragDetector mDragDetector;
private RelayoutParams mRelayoutParams = new RelayoutParams();
+ private final int mCaptionMenuWidthId = R.dimen.freeform_decor_caption_menu_width;
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
@@ -81,7 +81,6 @@
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mDesktopActive = DesktopModeStatus.isActive(mContext);
- mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
}
void setCaptionListeners(
@@ -91,12 +90,13 @@
mOnCaptionTouchListener = onCaptionTouchListener;
}
- void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
- mDragResizeCallback = dragResizeCallback;
+ void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
+ mDragPositioningCallback = dragPositioningCallback;
}
- DragDetector getDragDetector() {
- return mDragDetector;
+ void setDragDetector(DragDetector dragDetector) {
+ mDragDetector = dragDetector;
+ mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
}
@Override
@@ -131,22 +131,10 @@
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = R.layout.desktop_mode_window_decor;
mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
- mRelayoutParams.mCaptionWidthId = R.dimen.freeform_decor_caption_width;
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
if (isDragResizeable) {
mRelayoutParams.setOutsets(outsetLeftId, outsetTopId, outsetRightId, outsetBottomId);
}
- final Resources resources = mDecorWindowContext.getResources();
- final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
- final int captionHeight = loadDimensionPixelSize(resources,
- mRelayoutParams.mCaptionHeightId);
- final int captionWidth = loadDimensionPixelSize(resources,
- mRelayoutParams.mCaptionWidthId);
- final int captionLeft = taskBounds.width() / 2
- - captionWidth / 2;
- final int captionTop = taskBounds.top
- <= captionHeight / 2 ? 0 : -captionHeight / 2;
- mRelayoutParams.setCaptionPosition(captionLeft, captionTop);
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
@@ -191,7 +179,7 @@
mChoreographer,
mDisplay.getDisplayId(),
mDecorationContainerSurface,
- mDragResizeCallback);
+ mDragPositioningCallback);
}
final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
@@ -212,10 +200,6 @@
private void setupRootView() {
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
caption.setOnTouchListener(mOnCaptionTouchListener);
- final View close = caption.findViewById(R.id.close_window);
- close.setOnClickListener(mOnCaptionButtonClickListener);
- final View back = caption.findViewById(R.id.back_button);
- back.setOnClickListener(mOnCaptionButtonClickListener);
final View handle = caption.findViewById(R.id.caption_handle);
handle.setOnTouchListener(mOnCaptionTouchListener);
handle.setOnClickListener(mOnCaptionButtonClickListener);
@@ -230,8 +214,6 @@
desktop.setOnClickListener(mOnCaptionButtonClickListener);
final View split = menu.findViewById(R.id.split_screen_button);
split.setOnClickListener(mOnCaptionButtonClickListener);
- final View more = menu.findViewById(R.id.more_button);
- more.setOnClickListener(mOnCaptionButtonClickListener);
}
/**
@@ -264,10 +246,6 @@
void setButtonVisibility(boolean visible) {
final int visibility = visible ? View.VISIBLE : View.GONE;
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- final View back = caption.findViewById(R.id.back_button);
- final View close = caption.findViewById(R.id.close_window);
- back.setVisibility(visibility);
- close.setVisibility(visibility);
final int buttonTintColorRes =
mDesktopActive ? R.color.decor_button_dark_color
: R.color.decor_button_light_color;
@@ -297,14 +275,18 @@
void createHandleMenu() {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mDecorWindowContext.getResources();
- final int x = mRelayoutParams.mCaptionX;
- final int y = mRelayoutParams.mCaptionY;
- final int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
- final int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
+ final int captionWidth = mTaskInfo.getConfiguration()
+ .windowConfiguration.getBounds().width();
+ final int menuWidth = loadDimensionPixelSize(
+ resources, mCaptionMenuWidthId);
+ final int height = loadDimensionPixelSize(
+ resources, mRelayoutParams.mCaptionHeightId);
+ final int x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
+ - mResult.mDecorContainerOffsetX;
+ final int y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
String namePrefix = "Caption Menu";
- mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t,
- x - mResult.mDecorContainerOffsetX, y - mResult.mDecorContainerOffsetY,
- width, height);
+ mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y,
+ menuWidth, height);
mSyncQueue.runInSync(transaction -> {
transaction.merge(t);
t.close();
@@ -370,7 +352,7 @@
if (mResult.mRootView == null) return false;
final PointF inputPoint = offsetCaptionLocation(ev);
final View view = mResult.mRootView.findViewById(layoutId);
- return view != null && view.pointInView(inputPoint.x, inputPoint.y, 0);
+ return view != null && pointInView(view, inputPoint.x, inputPoint.y);
}
boolean checkTouchEventInHandle(MotionEvent ev) {
@@ -387,32 +369,39 @@
*/
void checkClickEvent(MotionEvent ev) {
if (mResult.mRootView == null) return;
- final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- final PointF inputPoint = offsetCaptionLocation(ev);
if (!isHandleMenuActive()) {
+ final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
final View handle = caption.findViewById(R.id.caption_handle);
- clickIfPointInView(inputPoint, handle);
+ clickIfPointInView(new PointF(ev.getX(), ev.getY()), handle);
} else {
final View menu = mHandleMenu.mWindowViewHost.getView();
+ final int captionWidth = mTaskInfo.getConfiguration().windowConfiguration
+ .getBounds().width();
+ final int menuX = mRelayoutParams.mCaptionX + (captionWidth / 2)
+ - (menu.getWidth() / 2);
+ final PointF inputPoint = new PointF(ev.getX() - menuX, ev.getY());
final View fullscreen = menu.findViewById(R.id.fullscreen_button);
if (clickIfPointInView(inputPoint, fullscreen)) return;
final View desktop = menu.findViewById(R.id.desktop_button);
if (clickIfPointInView(inputPoint, desktop)) return;
final View split = menu.findViewById(R.id.split_screen_button);
if (clickIfPointInView(inputPoint, split)) return;
- final View more = menu.findViewById(R.id.more_button);
- clickIfPointInView(inputPoint, more);
}
}
private boolean clickIfPointInView(PointF inputPoint, View v) {
- if (v.pointInView(inputPoint.x - v.getLeft(), inputPoint.y, 0)) {
+ if (pointInView(v, inputPoint.x, inputPoint.y)) {
mOnCaptionButtonClickListener.onClick(v);
return true;
}
return false;
}
+ private boolean pointInView(View v, float x, float y) {
+ return v != null && v.getLeft() <= x && v.getRight() >= x
+ && v.getTop() <= y && v.getBottom() >= y;
+ }
+
@Override
public void close() {
closeDragResizeListener();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 0abe8ab..4fac843 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
@@ -25,63 +26,82 @@
import android.view.MotionEvent;
/**
- * A detector for touch inputs that differentiates between drag and click inputs.
+ * A detector for touch inputs that differentiates between drag and click inputs. It receives a flow
+ * of {@link MotionEvent} and generates a new flow of motion events with slop in consideration to
+ * the event handler. In particular, it always passes down, up and cancel events. It'll pass move
+ * events only when there is at least one move event that's beyond the slop threshold. For the
+ * purpose of convenience it also passes all events of other actions.
+ *
* All touch events must be passed through this class to track a drag event.
*/
-public class DragDetector {
+class DragDetector {
+ private final MotionEventHandler mEventHandler;
+
+ private final PointF mInputDownPoint = new PointF();
private int mTouchSlop;
- private PointF mInputDownPoint;
private boolean mIsDragEvent;
private int mDragPointerId;
- public DragDetector(int touchSlop) {
- mTouchSlop = touchSlop;
- mInputDownPoint = new PointF();
- mIsDragEvent = false;
- mDragPointerId = -1;
+
+ private boolean mResultOfDownAction;
+
+ DragDetector(MotionEventHandler eventHandler) {
+ resetState();
+ mEventHandler = eventHandler;
}
/**
- * Determine if {@link MotionEvent} is part of a drag event.
- * @return {@code true} if this is a drag event, {@code false} if not
- */
- public boolean detectDragEvent(MotionEvent ev) {
- switch (ev.getAction()) {
+ * The receiver of the {@link MotionEvent} flow.
+ *
+ * @return the result returned by {@link #mEventHandler}, or the result when
+ * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
+ */
+ boolean onMotionEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
case ACTION_DOWN: {
+ // Only touch screens generate noisy moves.
+ mIsDragEvent = (ev.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
mDragPointerId = ev.getPointerId(0);
float rawX = ev.getRawX(0);
float rawY = ev.getRawY(0);
mInputDownPoint.set(rawX, rawY);
- return false;
+ mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
+ return mResultOfDownAction;
}
case ACTION_MOVE: {
if (!mIsDragEvent) {
int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
- if (Math.hypot(dx, dy) > mTouchSlop) {
- mIsDragEvent = true;
- }
+ mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
}
- return mIsDragEvent;
+ if (mIsDragEvent) {
+ return mEventHandler.handleMotionEvent(ev);
+ } else {
+ return mResultOfDownAction;
+ }
}
- case ACTION_UP: {
- boolean result = mIsDragEvent;
- mIsDragEvent = false;
- mInputDownPoint.set(0, 0);
- mDragPointerId = -1;
- return result;
- }
+ case ACTION_UP:
case ACTION_CANCEL: {
- mIsDragEvent = false;
- mInputDownPoint.set(0, 0);
- mDragPointerId = -1;
- return false;
+ resetState();
+ return mEventHandler.handleMotionEvent(ev);
}
+ default:
+ return mEventHandler.handleMotionEvent(ev);
}
- return mIsDragEvent;
}
- public void setTouchSlop(int touchSlop) {
+ void setTouchSlop(int touchSlop) {
mTouchSlop = touchSlop;
}
+
+ private void resetState() {
+ mIsDragEvent = false;
+ mInputDownPoint.set(0, 0);
+ mDragPointerId = -1;
+ mResultOfDownAction = false;
+ }
+
+ interface MotionEventHandler {
+ boolean handleMotionEvent(MotionEvent ev);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
similarity index 76%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index ee160a1..0191c60 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -19,28 +19,28 @@
/**
* Callback called when receiving drag-resize or drag-move related input events.
*/
-public interface DragResizeCallback {
+public interface DragPositioningCallback {
/**
- * Called when a drag resize starts.
+ * Called when a drag-resize or drag-move starts.
*
* @param ctrlType {@link TaskPositioner.CtrlType} indicating the direction of resizing, use
* {@code 0} to indicate it's a move
- * @param x x coordinate in window decoration coordinate system where the drag resize starts
- * @param y y coordinate in window decoration coordinate system where the drag resize starts
+ * @param x x coordinate in window decoration coordinate system where the drag starts
+ * @param y y coordinate in window decoration coordinate system where the drag starts
*/
- void onDragResizeStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
+ void onDragPositioningStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
/**
- * Called when the pointer moves during a drag resize.
+ * Called when the pointer moves during a drag-resize or drag-move.
* @param x x coordinate in window decoration coordinate system of the new pointer location
* @param y y coordinate in window decoration coordinate system of the new pointer location
*/
- void onDragResizeMove(float x, float y);
+ void onDragPositioningMove(float x, float y);
/**
- * Called when a drag resize stops.
+ * Called when a drag-resize or drag-move stops.
* @param x x coordinate in window decoration coordinate system where the drag resize stops
* @param y y coordinate in window decoration coordinate system where the drag resize stops
*/
- void onDragResizeEnd(float x, float y);
+ void onDragPositioningEnd(float x, float y);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index bb67145..3d1edc9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -48,7 +48,6 @@
* Task edges are for resizing with a mouse.
* Task corners are for resizing with touch input.
*/
-// TODO(b/251270585): investigate how to pass taps in corners to the tasks
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
@@ -63,7 +62,7 @@
private final SurfaceControl mDecorationSurface;
private final InputChannel mInputChannel;
private final TaskResizeInputEventReceiver mInputEventReceiver;
- private final com.android.wm.shell.windowdecor.DragResizeCallback mCallback;
+ private final DragPositioningCallback mCallback;
private int mWidth;
private int mHeight;
@@ -84,7 +83,7 @@
Choreographer choreographer,
int displayId,
SurfaceControl decorationSurface,
- DragResizeCallback callback) {
+ DragPositioningCallback callback) {
mInputManager = context.getSystemService(InputManager.class);
mHandler = handler;
mChoreographer = choreographer;
@@ -116,7 +115,8 @@
mInputEventReceiver = new TaskResizeInputEventReceiver(
mInputChannel, mHandler, mChoreographer);
mCallback = callback;
- mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
+ mDragDetector = new DragDetector(mInputEventReceiver);
+ mDragDetector.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
}
/**
@@ -224,12 +224,12 @@
}
}
- private class TaskResizeInputEventReceiver extends InputEventReceiver {
+ private class TaskResizeInputEventReceiver extends InputEventReceiver
+ implements DragDetector.MotionEventHandler {
private final Choreographer mChoreographer;
private final Runnable mConsumeBatchEventRunnable;
private boolean mConsumeBatchEventScheduled;
private boolean mShouldHandleEvents;
- private boolean mDragging;
private TaskResizeInputEventReceiver(
InputChannel inputChannel, Handler handler, Choreographer choreographer) {
@@ -271,15 +271,15 @@
if (!(inputEvent instanceof MotionEvent)) {
return false;
}
+ return mDragDetector.onMotionEvent((MotionEvent) inputEvent);
+ }
- MotionEvent e = (MotionEvent) inputEvent;
+ @Override
+ public boolean handleMotionEvent(MotionEvent e) {
boolean result = false;
// Check if this is a touch event vs mouse event.
// Touch events are tracked in four corners. Other events are tracked in resize edges.
boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
- if (isTouch) {
- mDragging = mDragDetector.detectDragEvent(e);
- }
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
float x = e.getX(0);
@@ -294,7 +294,7 @@
float rawX = e.getRawX(0);
float rawY = e.getRawY(0);
int ctrlType = calculateCtrlType(isTouch, x, y);
- mCallback.onDragResizeStart(ctrlType, rawX, rawY);
+ mCallback.onDragPositioningStart(ctrlType, rawX, rawY);
result = true;
}
break;
@@ -306,24 +306,17 @@
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
float rawX = e.getRawX(dragPointerIndex);
float rawY = e.getRawY(dragPointerIndex);
- if (!isTouch) {
- // For all other types allow immediate dragging.
- mDragging = true;
- }
- if (mDragging) {
- mCallback.onDragResizeMove(rawX, rawY);
- result = true;
- }
+ mCallback.onDragPositioningMove(rawX, rawY);
+ result = true;
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
- if (mShouldHandleEvents && mDragging) {
+ if (mShouldHandleEvents) {
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
- mCallback.onDragResizeEnd(
+ mCallback.onDragPositioningEnd(
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
}
- mDragging = false;
mShouldHandleEvents = false;
mDragPointerId = -1;
result = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index 20631f8..d3f9227 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -23,7 +23,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
-class TaskPositioner implements DragResizeCallback {
+class TaskPositioner implements DragPositioningCallback {
@IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
@interface CtrlType {}
@@ -38,11 +38,9 @@
private final WindowDecoration mWindowDecoration;
private final Rect mTaskBoundsAtDragStart = new Rect();
- private final PointF mResizeStartPoint = new PointF();
- private final Rect mResizeTaskBounds = new Rect();
- // Whether the |dragResizing| hint should be sent with the next bounds change WCT.
- // Used to optimized fluid resizing of freeform tasks.
- private boolean mPendingDragResizeHint = false;
+ private final PointF mRepositionStartPoint = new PointF();
+ private final Rect mRepositionTaskBounds = new Rect();
+ private boolean mHasMoved = false;
private int mCtrlType;
private DragStartListener mDragStartListener;
@@ -59,72 +57,84 @@
}
@Override
- public void onDragResizeStart(int ctrlType, float x, float y) {
- if (ctrlType != CTRL_TYPE_UNDEFINED) {
- // The task is being resized, send the |dragResizing| hint to core with the first
- // bounds-change wct.
- mPendingDragResizeHint = true;
- }
+ public void onDragPositioningStart(int ctrlType, float x, float y) {
+ mHasMoved = false;
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
mCtrlType = ctrlType;
mTaskBoundsAtDragStart.set(
mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
- mResizeStartPoint.set(x, y);
+ mRepositionStartPoint.set(x, y);
}
@Override
- public void onDragResizeMove(float x, float y) {
+ public void onDragPositioningMove(float x, float y) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (changeBounds(wct, x, y)) {
- if (mPendingDragResizeHint) {
+ // The task is being resized, send the |dragResizing| hint to core with the first
+ // bounds-change wct.
+ if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) {
// This is the first bounds change since drag resize operation started.
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
- mPendingDragResizeHint = false;
}
mTaskOrganizer.applyTransaction(wct);
+ mHasMoved = true;
}
}
@Override
- public void onDragResizeEnd(float x, float y) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
- changeBounds(wct, x, y);
- mTaskOrganizer.applyTransaction(wct);
+ public void onDragPositioningEnd(float x, float y) {
+ // |mHasMoved| being false means there is no real change to the task bounds in WM core, so
+ // we don't need a WCT to finish it.
+ if (mHasMoved) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
+ changeBounds(wct, x, y);
+ mTaskOrganizer.applyTransaction(wct);
+ }
- mCtrlType = 0;
+ mCtrlType = CTRL_TYPE_UNDEFINED;
mTaskBoundsAtDragStart.setEmpty();
- mResizeStartPoint.set(0, 0);
- mPendingDragResizeHint = false;
+ mRepositionStartPoint.set(0, 0);
+ mHasMoved = false;
}
private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
- float deltaX = x - mResizeStartPoint.x;
- mResizeTaskBounds.set(mTaskBoundsAtDragStart);
+ // |mRepositionTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not
+ // true, we can compare it against |mTaskBoundsAtDragStart|.
+ final int oldLeft = mHasMoved ? mRepositionTaskBounds.left : mTaskBoundsAtDragStart.left;
+ final int oldTop = mHasMoved ? mRepositionTaskBounds.top : mTaskBoundsAtDragStart.top;
+ final int oldRight = mHasMoved ? mRepositionTaskBounds.right : mTaskBoundsAtDragStart.right;
+ final int oldBottom =
+ mHasMoved ? mRepositionTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
+
+ final float deltaX = x - mRepositionStartPoint.x;
+ final float deltaY = y - mRepositionStartPoint.y;
+ mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
- mResizeTaskBounds.left += deltaX;
+ mRepositionTaskBounds.left += deltaX;
}
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
- mResizeTaskBounds.right += deltaX;
+ mRepositionTaskBounds.right += deltaX;
}
- float deltaY = y - mResizeStartPoint.y;
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
- mResizeTaskBounds.top += deltaY;
+ mRepositionTaskBounds.top += deltaY;
}
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
- mResizeTaskBounds.bottom += deltaY;
+ mRepositionTaskBounds.bottom += deltaY;
}
- if (mCtrlType == 0) {
- mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
+ if (mCtrlType == CTRL_TYPE_UNDEFINED) {
+ mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
}
- if (!mResizeTaskBounds.isEmpty()) {
- wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
- return true;
+ if (oldLeft == mRepositionTaskBounds.left && oldTop == mRepositionTaskBounds.top
+ && oldRight == mRepositionTaskBounds.right
+ && oldBottom == mRepositionTaskBounds.bottom) {
+ return false;
}
- return false;
+ wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
+ return true;
}
interface DragStartListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7f85988..62b72f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -252,9 +252,7 @@
}
final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
- final int captionWidth = params.mCaptionWidthId == Resources.ID_NULL
- ? taskBounds.width()
- : loadDimensionPixelSize(resources, params.mCaptionWidthId);
+ final int captionWidth = taskBounds.width();
startT.setPosition(
mCaptionContainerSurface,
@@ -428,7 +426,6 @@
RunningTaskInfo mRunningTaskInfo;
int mLayoutResId;
int mCaptionHeightId;
- int mCaptionWidthId;
int mShadowRadiusId;
int mOutsetTopId;
@@ -454,7 +451,6 @@
void reset() {
mLayoutResId = Resources.ID_NULL;
mCaptionHeightId = Resources.ID_NULL;
- mCaptionWidthId = Resources.ID_NULL;
mShadowRadiusId = Resources.ID_NULL;
mOutsetTopId = Resources.ID_NULL;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 85812c4..8c3bea8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -23,6 +23,7 @@
import com.android.server.wm.flicker.FlickerBuilder
import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -30,6 +31,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,7 +73,19 @@
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
- @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+ @FlakyTest
+ @Test
+ fun primaryAppLayerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesVisibleShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 7c62433..06a1449 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -23,6 +23,7 @@
import com.android.server.wm.flicker.FlickerBuilder
import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -30,6 +31,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,7 +73,19 @@
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
- @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+ @FlakyTest
+ @Test
+ fun primaryAppLayerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesVisibleShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
index 8949a75..27d40b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
@@ -17,7 +17,7 @@
<resources>
<!-- Resources used in WindowDecorationTests -->
<dimen name="test_freeform_decor_caption_height">32dp</dimen>
- <dimen name="test_freeform_decor_caption_width">216dp</dimen>
+ <dimen name="test_freeform_decor_caption_menu_width">216dp</dimen>
<dimen name="test_window_decor_left_outset">10dp</dimen>
<dimen name="test_window_decor_top_outset">20dp</dimen>
<dimen name="test_window_decor_right_outset">30dp</dimen>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index d5bb901..62bfd17 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -92,6 +92,7 @@
Context mContext;
TaskView mTaskView;
TaskViewTransitions mTaskViewTransitions;
+ TaskViewTaskController mTaskViewTaskController;
@Before
public void setUp() {
@@ -125,7 +126,9 @@
doReturn(true).when(mTransitions).isRegistered();
}
mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
- mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
+ mTaskViewTaskController = new TaskViewTaskController(mContext, mOrganizer,
+ mTaskViewTransitions, mSyncQueue);
+ mTaskView = new TaskView(mContext, mTaskViewTaskController);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -138,7 +141,8 @@
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
+ TaskView taskView = new TaskView(mContext,
+ new TaskViewTaskController(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue));
taskView.setListener(mExecutor, mViewListener);
try {
taskView.setListener(mExecutor, mViewListener);
@@ -152,16 +156,17 @@
@Test
public void testStartActivity() {
ActivityOptions options = ActivityOptions.makeBasic();
- mTaskView.startActivity(mock(PendingIntent.class), null, options, new Rect(0, 0, 100, 100));
+ mTaskView.startActivity(mock(PendingIntent.class), null, options,
+ new Rect(0, 0, 100, 100));
- verify(mOrganizer).setPendingLaunchCookieListener(any(), eq(mTaskView));
+ verify(mOrganizer).setPendingLaunchCookieListener(any(), eq(mTaskViewTaskController));
assertThat(options.getLaunchWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW);
}
@Test
public void testOnTaskAppeared_noSurface_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
verify(mViewListener, never()).onInitialized();
@@ -173,7 +178,7 @@
public void testOnTaskAppeared_withSurface_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
assertThat(mTaskView.isInitialized()).isTrue();
@@ -194,7 +199,7 @@
@Test
public void testSurfaceCreated_withTask_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
@@ -216,7 +221,7 @@
public void testSurfaceDestroyed_withTask_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(sh);
reset(mViewListener);
mTaskView.surfaceDestroyed(sh);
@@ -227,11 +232,11 @@
@Test
public void testOnReleased_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.release();
- verify(mOrganizer).removeListener(eq(mTaskView));
+ verify(mOrganizer).removeListener(eq(mTaskViewTaskController));
verify(mViewListener).onReleased();
assertThat(mTaskView.isInitialized()).isFalse();
}
@@ -239,9 +244,9 @@
@Test
public void testOnTaskVanished_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- mTaskView.onTaskVanished(mTaskInfo);
+ mTaskViewTaskController.onTaskVanished(mTaskInfo);
verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
}
@@ -249,8 +254,8 @@
@Test
public void testOnBackPressedOnTaskRoot_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
- mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onBackPressedOnTaskRoot(mTaskInfo);
verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
}
@@ -258,17 +263,17 @@
@Test
public void testSetOnBackPressedOnTaskRoot_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
@Test
public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
- mTaskView.onTaskVanished(mTaskInfo);
+ mTaskViewTaskController.onTaskVanished(mTaskInfo);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
}
@@ -276,8 +281,9 @@
public void testOnNewTask_noSurface() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
verify(mViewListener, never()).onInitialized();
@@ -303,8 +309,9 @@
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
@@ -314,15 +321,17 @@
public void testSurfaceCreated_withTask() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
- verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(true));
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskViewTaskController), eq(true));
- mTaskView.prepareOpenAnimation(false /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(false /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
}
@@ -342,15 +351,16 @@
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(sh);
reset(mViewListener);
mTaskView.surfaceDestroyed(sh);
- verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(false));
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskViewTaskController), eq(false));
- mTaskView.prepareHideAnimation(new SurfaceControl.Transaction());
+ mTaskViewTaskController.prepareHideAnimation(new SurfaceControl.Transaction());
verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
}
@@ -359,25 +369,27 @@
public void testOnReleased() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.release();
- verify(mOrganizer).removeListener(eq(mTaskView));
+ verify(mOrganizer).removeListener(eq(mTaskViewTaskController));
verify(mViewListener).onReleased();
assertThat(mTaskView.isInitialized()).isFalse();
- verify(mTaskViewTransitions).removeTaskView(eq(mTaskView));
+ verify(mTaskViewTransitions).removeTaskView(eq(mTaskViewTaskController));
}
@Test
public void testOnTaskVanished() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- mTaskView.prepareCloseAnimation();
+ mTaskViewTaskController.prepareCloseAnimation();
verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
}
@@ -386,9 +398,10 @@
public void testOnBackPressedOnTaskRoot() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
- mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+ mTaskViewTaskController.onBackPressedOnTaskRoot(mTaskInfo);
verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
}
@@ -397,8 +410,9 @@
public void testSetOnBackPressedOnTaskRoot() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
@@ -406,11 +420,12 @@
public void testUnsetOnBackPressedOnTaskRoot() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
- mTaskView.prepareCloseAnimation();
+ mTaskViewTaskController.prepareCloseAnimation();
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
index 1636c5f..0a31338 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -21,7 +21,6 @@
import android.util.SparseArray
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.bubbles.storage.BubbleXmlHelperTest.Companion.sparseArraysEqual
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertTrue
@@ -36,7 +35,8 @@
// user, package, shortcut, notification key, height, res-height, title, taskId, locusId
private val user0Bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1, null),
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1, null,
+ true),
BubbleEntity(10, "com.example.chat", "alice and bob", "0k2", 0, 16537428, "title", 2,
null),
BubbleEntity(0, "com.example.messenger", "shortcut-2", "0k3", 120, 0, null,
@@ -44,7 +44,8 @@
)
private val user1Bubbles = listOf(
- BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3, null),
+ BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3, null,
+ true),
BubbleEntity(12, "com.example.chat", "alice and bob", "1k2", 0, 16537428, "title", 4,
null),
BubbleEntity(1, "com.example.messenger", "shortcut-2", "1k3", 120, 0, null,
@@ -76,6 +77,6 @@
assertEquals(actual.size(), 0)
repository.persistsToDisk(bubbles)
- assertTrue(sparseArraysEqual(bubbles, repository.readFromDisk()))
+ assertTrue(bubbles.contentEquals(repository.readFromDisk()))
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
index 4ab9f87..c02e2d1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
@@ -34,7 +34,8 @@
class BubbleXmlHelperTest : ShellTestCase() {
private val user0Bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1),
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1,
+ isClearable = true),
BubbleEntity(10, "com.example.chat", "alice and bob", "0k2", 0, 16537428, "title", 2,
null),
BubbleEntity(0, "com.example.messenger", "shortcut-2", "0k3", 120, 0, null,
@@ -42,7 +43,8 @@
)
private val user1Bubbles = listOf(
- BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3),
+ BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3,
+ isClearable = true),
BubbleEntity(12, "com.example.chat", "alice and bob", "1k2", 0, 16537428, "title", 4,
null),
BubbleEntity(1, "com.example.messenger", "shortcut-2", "1k3", 120, 0, null,
@@ -51,28 +53,6 @@
private val bubbles = SparseArray<List<BubbleEntity>>()
- // Checks that the contents of the two sparse arrays are the same.
- companion object {
- fun sparseArraysEqual(
- one: SparseArray<List<BubbleEntity>>?,
- two: SparseArray<List<BubbleEntity>>?
- ): Boolean {
- if (one == null && two == null) return true
- if ((one == null) != (two == null)) return false
- if (one!!.size() != two!!.size()) return false
- for (i in 0 until one.size()) {
- val k1 = one.keyAt(i)
- val v1 = one.valueAt(i)
- val k2 = two.keyAt(i)
- val v2 = two.valueAt(i)
- if (k1 != k2 && v1 != v2) {
- return false
- }
- }
- return true
- }
- }
-
@Before
fun setup() {
bubbles.put(0, user0Bubbles)
@@ -83,14 +63,14 @@
fun testWriteXml() {
val expectedEntries = """
<bs uid="0">
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" d="true" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" d="false" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" d="false" />
</bs>
<bs uid="1">
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" />
-<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" />
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" d="true" />
+<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" d="false" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" d="false" />
</bs>
""".trimIndent()
ByteArrayOutputStream().use {
@@ -107,19 +87,19 @@
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<bs v="2">
<bs uid="0">
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" d="true" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" d="false" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" d="false" />
</bs>
<bs uid="1">
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" />
-<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" />
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" d="true" />
+<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" d="false" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" d="false" />
</bs>
</bs>
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
- assertTrue("failed parsing bubbles from xml\n$src", sparseArraysEqual(bubbles, actual))
+ assertTrue("failed parsing bubbles from xml\n$src", bubbles.contentEquals(actual))
}
// V0 -> V1 happened prior to release / during dogfood so nothing is saved
@@ -161,8 +141,7 @@
</bs>
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
- assertTrue("failed parsing bubbles from xml\n$src",
- sparseArraysEqual(expectedBubbles, actual))
+ assertTrue("failed parsing bubbles from xml\n$src", expectedBubbles.contentEquals(actual))
}
/**
@@ -187,7 +166,7 @@
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
assertTrue("failed parsing bubbles from xml\n$src",
- sparseArraysEqual(expectedBubbles, actual))
+ expectedBubbles.contentEquals(actual))
}
@Test
@@ -210,6 +189,6 @@
)
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
assertTrue("failed parsing bubbles from xml\n$src",
- sparseArraysEqual(expectedBubbles, actual))
+ expectedBubbles.contentEquals(actual))
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index d4408d7..05fd889 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -95,7 +95,10 @@
private @Mock Lazy<Transitions> mMockTransitionsLazy;
private @Mock CompatUIWindowManager mMockCompatLayout;
private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
+ private @Mock RestartDialogWindowManager mMockRestartDialogLayout;
private @Mock DockStateReader mDockStateReader;
+ private @Mock CompatUIConfiguration mCompatUIConfiguration;
+ private @Mock CompatUIShellCommandHandler mCompatUIShellCommandHandler;
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -113,10 +116,17 @@
doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
+
+ doReturn(DISPLAY_ID).when(mMockRestartDialogLayout).getDisplayId();
+ doReturn(TASK_ID).when(mMockRestartDialogLayout).getTaskId();
+ doReturn(true).when(mMockRestartDialogLayout).createLayout(anyBoolean());
+ doReturn(true).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
+
mShellInit = spy(new ShellInit(mMockExecutor));
mController = new CompatUIController(mContext, mShellInit, mMockShellController,
mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
- mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader) {
+ mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
+ mCompatUIConfiguration, mCompatUIShellCommandHandler) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
@@ -128,6 +138,12 @@
TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
return mMockLetterboxEduLayout;
}
+
+ @Override
+ RestartDialogWindowManager createRestartDialogWindowManager(Context context,
+ TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ return mMockRestartDialogLayout;
+ }
};
mShellInit.init();
spyOn(mController);
@@ -160,6 +176,8 @@
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
+ verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
// Verify that the compat controls and letterbox education are updated with new size compat
// info.
@@ -168,10 +186,12 @@
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- true);
- verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- true);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ true);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ true);
+ verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ true);
// Verify that compat controls and letterbox education are removed with null task listener.
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -181,12 +201,14 @@
verify(mMockCompatLayout).release();
verify(mMockLetterboxEduLayout).release();
+ verify(mMockRestartDialogLayout).release();
}
@Test
public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
doReturn(false).when(mMockCompatLayout).createLayout(anyBoolean());
doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
+ doReturn(false).when(mMockRestartDialogLayout).createLayout(anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -195,6 +217,8 @@
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
+ verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
// Verify that the layout is created again.
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -202,15 +226,19 @@
verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+ verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
+ verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
}
@Test
public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
doReturn(false).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
+ doReturn(false).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -219,24 +247,33 @@
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
+ verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
- clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
+ mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- true);
- verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- true);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ true);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ true);
+ verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ true);
// Verify that the layout is created again.
- clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
+ mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+ verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
+ verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
}
@@ -260,6 +297,7 @@
verify(mMockCompatLayout, never()).release();
verify(mMockLetterboxEduLayout, never()).release();
+ verify(mMockRestartDialogLayout, never()).release();
verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
any());
@@ -268,6 +306,7 @@
verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
verify(mMockCompatLayout).release();
verify(mMockLetterboxEduLayout).release();
+ verify(mMockRestartDialogLayout).release();
}
@Test
@@ -279,11 +318,13 @@
verify(mMockCompatLayout, never()).updateDisplayLayout(any());
verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(any());
+ verify(mMockRestartDialogLayout, never()).updateDisplayLayout(any());
mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
}
@Test
@@ -302,12 +343,14 @@
verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
// No update if the insets state is the same.
- clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockRestartDialogLayout, never()).updateDisplayLayout(mMockDisplayLayout);
}
@Test
@@ -320,22 +363,26 @@
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
+ verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- false);
- verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- false);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ false);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ false);
+ verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ false);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
+ verify(mMockRestartDialogLayout).updateVisibility(true);
}
@Test
@@ -348,22 +395,26 @@
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
+ verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- false);
- verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
- false);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ false);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ false);
+ verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+ /* canShow= */ false);
// Verify button is shown after keyguard becomes not showing.
mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
+ verify(mMockRestartDialogLayout).updateVisibility(true);
}
@Test
@@ -376,20 +427,23 @@
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
+ verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
- clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
// Verify button remains hidden after keyguard becomes not showing since IME is showing.
mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
+ verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button is shown after IME is not showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
+ verify(mMockRestartDialogLayout).updateVisibility(true);
}
@Test
@@ -402,20 +456,23 @@
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
+ verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
- clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
// Verify button remains hidden after IME is hidden since keyguard is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
+ verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button is shown after keyguard becomes not showing.
mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
+ verify(mMockRestartDialogLayout).updateVisibility(true);
}
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 7d3e718..5f294d5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -31,6 +31,7 @@
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.testing.AndroidTestingRunner;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.SurfaceControlViewHost;
import android.widget.ImageButton;
@@ -45,12 +46,17 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
+import junit.framework.Assert;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.function.Consumer;
+
/**
* Tests for {@link CompatUILayout}.
*
@@ -65,20 +71,22 @@
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock private CompatUIController.CompatUICallback mCallback;
+ @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private SurfaceControlViewHost mViewHost;
+ @Mock private CompatUIConfiguration mCompatUIConfiguration;
private CompatUIWindowManager mWindowManager;
private CompatUILayout mLayout;
+ private TaskInfo mTaskInfo;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- mWindowManager = new CompatUIWindowManager(mContext,
- createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
- mSyncTransactionQueue, mCallback, mTaskListener,
- new DisplayLayout(), new CompatUIHintsState());
+ mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
+ mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+ mCompatUIConfiguration, mOnRestartButtonClicked);
mLayout = (CompatUILayout)
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
@@ -95,8 +103,15 @@
final ImageButton button = mLayout.findViewById(R.id.size_compat_restart_button);
button.performClick();
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
+ ArgumentCaptor.forClass(Pair.class);
+
verify(mWindowManager).onRestartButtonClicked();
- verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+ verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
+ final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
+ Assert.assertEquals(mTaskInfo, result.first);
+ Assert.assertEquals(mTaskListener, result.second);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index c4d78bb..f3f615d9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -29,6 +29,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -39,6 +40,7 @@
import android.app.TaskInfo;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
+import android.util.Pair;
import android.view.DisplayInfo;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -54,12 +56,17 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
+import junit.framework.Assert;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.function.Consumer;
+
/**
* Tests for {@link CompatUIWindowManager}.
*
@@ -74,20 +81,22 @@
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock private CompatUIController.CompatUICallback mCallback;
+ @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private CompatUILayout mLayout;
@Mock private SurfaceControlViewHost mViewHost;
+ @Mock private CompatUIConfiguration mCompatUIConfiguration;
private CompatUIWindowManager mWindowManager;
+ private TaskInfo mTaskInfo;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- mWindowManager = new CompatUIWindowManager(mContext,
- createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
- mSyncTransactionQueue, mCallback, mTaskListener,
- new DisplayLayout(), new CompatUIHintsState());
+ mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
+ mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+ mCompatUIConfiguration, mOnRestartButtonClicked);
spyOn(mWindowManager);
doReturn(mLayout).when(mWindowManager).inflateLayout();
@@ -352,14 +361,14 @@
mWindowManager.updateVisibility(/* canShow= */ false);
verify(mWindowManager, never()).createLayout(anyBoolean());
- verify(mLayout).setVisibility(View.GONE);
+ verify(mLayout, atLeastOnce()).setVisibility(View.GONE);
// Show button.
doReturn(View.GONE).when(mLayout).getVisibility();
mWindowManager.updateVisibility(/* canShow= */ true);
verify(mWindowManager, never()).createLayout(anyBoolean());
- verify(mLayout).setVisibility(View.VISIBLE);
+ verify(mLayout, atLeastOnce()).setVisibility(View.VISIBLE);
}
@Test
@@ -405,7 +414,14 @@
public void testOnRestartButtonClicked() {
mWindowManager.onRestartButtonClicked();
- verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
+ ArgumentCaptor.forClass(Pair.class);
+
+ verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
+ final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
+ Assert.assertEquals(mTaskInfo, result.first);
+ Assert.assertEquals(mTaskListener, result.second);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
new file mode 100644
index 0000000..e2dcdb0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link RestartDialogLayout}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:RestartDialogLayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class RestartDialogLayoutTest extends ShellTestCase {
+
+ @Mock private Runnable mDismissCallback;
+ @Mock private Consumer<Boolean> mRestartCallback;
+
+ private RestartDialogLayout mLayout;
+ private View mDismissButton;
+ private View mRestartButton;
+ private View mDialogContainer;
+ private CheckBox mDontRepeatCheckBox;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mLayout = (RestartDialogLayout)
+ LayoutInflater.from(mContext).inflate(R.layout.letterbox_restart_dialog_layout,
+ null);
+ mDismissButton = mLayout.findViewById(R.id.letterbox_restart_dialog_dismiss_button);
+ mRestartButton = mLayout.findViewById(R.id.letterbox_restart_dialog_restart_button);
+ mDialogContainer = mLayout.findViewById(R.id.letterbox_restart_dialog_container);
+ mDontRepeatCheckBox = mLayout.findViewById(R.id.letterbox_restart_dialog_checkbox);
+ mLayout.setDismissOnClickListener(mDismissCallback);
+ mLayout.setRestartOnClickListener(mRestartCallback);
+ }
+
+ @Test
+ public void testOnFinishInflate() {
+ assertEquals(mLayout.getDialogContainerView(),
+ mLayout.findViewById(R.id.letterbox_restart_dialog_container));
+ assertEquals(mLayout.getDialogTitle(),
+ mLayout.findViewById(R.id.letterbox_restart_dialog_title));
+ assertEquals(mLayout.getBackgroundDimDrawable(), mLayout.getBackground());
+ assertEquals(mLayout.getBackground().getAlpha(), 0);
+ }
+
+ @Test
+ public void testOnDismissButtonClicked() {
+ assertTrue(mDismissButton.performClick());
+
+ verify(mDismissCallback).run();
+ }
+
+ @Test
+ public void testOnRestartButtonClickedWithoutCheckbox() {
+ mDontRepeatCheckBox.setChecked(false);
+ assertTrue(mRestartButton.performClick());
+
+ verify(mRestartCallback).accept(false);
+ }
+
+ @Test
+ public void testOnRestartButtonClickedWithCheckbox() {
+ mDontRepeatCheckBox.setChecked(true);
+ assertTrue(mRestartButton.performClick());
+
+ verify(mRestartCallback).accept(true);
+ }
+
+ @Test
+ public void testOnBackgroundClickedDoesntDismiss() {
+ assertFalse(mLayout.performClick());
+
+ verify(mDismissCallback, never()).run();
+ }
+
+ @Test
+ public void testOnDialogContainerClicked() {
+ assertTrue(mDialogContainer.performClick());
+
+ verify(mDismissCallback, never()).run();
+ verify(mRestartCallback, never()).accept(anyBoolean());
+ }
+
+ @Test
+ public void testSetDismissOnClickListenerNull() {
+ mLayout.setDismissOnClickListener(null);
+
+ assertFalse(mDismissButton.performClick());
+ assertFalse(mLayout.performClick());
+ assertTrue(mDialogContainer.performClick());
+
+ verify(mDismissCallback, never()).run();
+ }
+
+ @Test
+ public void testSetRestartOnClickListenerNull() {
+ mLayout.setRestartOnClickListener(null);
+
+ assertFalse(mRestartButton.performClick());
+ assertFalse(mLayout.performClick());
+ assertTrue(mDialogContainer.performClick());
+
+ verify(mRestartCallback, never()).accept(anyBoolean());
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 298d0a6..3d8cd2e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -32,6 +32,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import org.junit.Before;
import org.junit.Test;
@@ -57,17 +58,22 @@
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private DisplayInfo mDefaultDisplayInfo;
private PipBoundsState mPipBoundsState;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
@Before
public void setUp() throws Exception {
initializeMockResources();
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
+ mPipSizeSpecHandler);
- mPipBoundsState.setDisplayLayout(
- new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
+ DisplayLayout layout =
+ new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true);
+ mPipBoundsState.setDisplayLayout(layout);
+ mPipSizeSpecHandler.setDisplayLayout(layout);
}
private void initializeMockResources() {
@@ -120,9 +126,7 @@
@Test
public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() {
- final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO,
- DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth,
- mDefaultDisplayInfo.logicalHeight);
+ final Size defaultSize = mPipSizeSpecHandler.getDefaultSize(DEFAULT_ASPECT_RATIO);
mPipBoundsState.setOverrideMinSize(null);
final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 8e30f65..ede5bde 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -33,6 +33,7 @@
import com.android.internal.util.function.TriConsumer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import org.junit.Before;
import org.junit.Test;
@@ -57,7 +58,7 @@
@Before
public void setUp() {
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, new PipSizeSpecHandler(mContext));
mTestComponentName1 = new ComponentName(mContext, "component1");
mTestComponentName2 = new ComponentName(mContext, "component2");
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 17e7d74..2da4af8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -53,6 +53,7 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
@@ -86,6 +87,7 @@
private PipBoundsState mPipBoundsState;
private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
private ComponentName mComponent1;
private ComponentName mComponent2;
@@ -95,10 +97,12 @@
MockitoAnnotations.initMocks(this);
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
+ mPipSizeSpecHandler);
mMainExecutor = new TestShellExecutor();
mPipTaskOrganizer = new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
@@ -253,8 +257,10 @@
private void preparePipTaskOrg() {
final DisplayInfo info = new DisplayInfo();
- mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
- mContext.getResources(), true, true));
+ DisplayLayout layout = new DisplayLayout(info,
+ mContext.getResources(), true, true);
+ mPipBoundsState.setDisplayLayout(layout);
+ mPipSizeSpecHandler.setDisplayLayout(layout);
mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 35c09a1..4a68287 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -65,7 +65,6 @@
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -108,6 +107,7 @@
@Mock private PipMotionHelper mMockPipMotionHelper;
@Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
@Mock private PipBoundsState mMockPipBoundsState;
+ @Mock private PipSizeSpecHandler mMockPipSizeSpecHandler;
@Mock private TaskStackListenerImpl mMockTaskStackListener;
@Mock private ShellExecutor mMockExecutor;
@Mock private Optional<OneHandedController> mMockOneHandedController;
@@ -130,11 +130,12 @@
mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
mShellController, mMockDisplayController, mMockPipAnimationController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
- mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
- mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
- mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mMockPipParamsChangedForwarder,
- mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor);
+ mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
+ mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
+ mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
+ mMockWindowManagerShellWrapper, mMockTaskStackListener,
+ mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
+ mMockOneHandedController, mMockExecutor);
mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -220,11 +221,12 @@
assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
mShellController, mMockDisplayController, mMockPipAnimationController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
- mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
- mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
- mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mMockPipParamsChangedForwarder,
- mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor));
+ mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
+ mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
+ mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
+ mMockWindowManagerShellWrapper, mMockTaskStackListener,
+ mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
+ mMockOneHandedController, mMockExecutor));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index c1993b2..c7b9eb3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -85,15 +85,18 @@
private PipBoundsState mPipBoundsState;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
final PipKeepClearAlgorithmInterface pipKeepClearAlgorithm =
new PipKeepClearAlgorithmInterface() {};
final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
- mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm);
+ mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm, mPipSizeSpecHandler);
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
new file mode 100644
index 0000000..d9ff7d1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.SystemProperties;
+import android.testing.AndroidTestingRunner;
+import android.util.Size;
+import android.view.DisplayInfo;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.exceptions.misusing.InvalidUseOfMatchersException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * Unit test against {@link PipSizeSpecHandler} with feature flag on.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipSizeSpecHandlerTest extends ShellTestCase {
+ /** A sample overridden min edge size. */
+ private static final int OVERRIDE_MIN_EDGE_SIZE = 40;
+ /** A sample default min edge size */
+ private static final int DEFAULT_MIN_EDGE_SIZE = 40;
+ /** Display edge size */
+ private static final int DISPLAY_EDGE_SIZE = 1000;
+ /** Default sizing percentage */
+ private static final float DEFAULT_PERCENT = 0.6f;
+ /** Minimum sizing percentage */
+ private static final float MIN_PERCENT = 0.5f;
+ /** Aspect ratio that the new PIP size spec logic optimizes for. */
+ private static final float OPTIMIZED_ASPECT_RATIO = 9f / 16;
+
+ /** A map of aspect ratios to be tested to expected sizes */
+ private static Map<Float, Size> sExpectedMaxSizes;
+ private static Map<Float, Size> sExpectedDefaultSizes;
+ private static Map<Float, Size> sExpectedMinSizes;
+ /** A static mockito session object to mock {@link SystemProperties} */
+ private static StaticMockitoSession sStaticMockitoSession;
+
+ @Mock private Context mContext;
+ @Mock private Resources mResources;
+
+ private PipSizeSpecHandler mPipSizeSpecHandler;
+
+ /**
+ * Sets up static Mockito session for SystemProperties and mocks necessary static methods.
+ */
+ private static void setUpStaticSystemPropertiesSession() {
+ sStaticMockitoSession = mockitoSession()
+ .mockStatic(SystemProperties.class).startMocking();
+ // make sure the feature flag is on
+ when(SystemProperties.getBoolean(anyString(), anyBoolean())).thenReturn(true);
+ when(SystemProperties.get(anyString(), anyString())).thenAnswer(invocation -> {
+ String property = invocation.getArgument(0);
+ if (property.equals("com.android.wm.shell.pip.phone.def_percentage")) {
+ return Float.toString(DEFAULT_PERCENT);
+ } else if (property.equals("com.android.wm.shell.pip.phone.min_percentage")) {
+ return Float.toString(MIN_PERCENT);
+ }
+
+ // throw an exception if illegal arguments are used for these tests
+ throw new InvalidUseOfMatchersException(
+ String.format("Argument %s does not match", property)
+ );
+ });
+ }
+
+ /**
+ * Initializes the map with the aspect ratios to be tested and corresponding expected max sizes.
+ */
+ private static void initExpectedSizes() {
+ sExpectedMaxSizes = new HashMap<>();
+ sExpectedDefaultSizes = new HashMap<>();
+ sExpectedMinSizes = new HashMap<>();
+
+ sExpectedMaxSizes.put(16f / 9, new Size(1000, 562));
+ sExpectedDefaultSizes.put(16f / 9, new Size(600, 337));
+ sExpectedMinSizes.put(16f / 9, new Size(499, 281));
+
+ sExpectedMaxSizes.put(4f / 3, new Size(892, 669));
+ sExpectedDefaultSizes.put(4f / 3, new Size(535, 401));
+ sExpectedMinSizes.put(4f / 3, new Size(445, 334));
+
+ sExpectedMaxSizes.put(3f / 4, new Size(669, 892));
+ sExpectedDefaultSizes.put(3f / 4, new Size(401, 535));
+ sExpectedMinSizes.put(3f / 4, new Size(334, 445));
+
+ sExpectedMaxSizes.put(9f / 16, new Size(562, 999));
+ sExpectedDefaultSizes.put(9f / 16, new Size(337, 599));
+ sExpectedMinSizes.put(9f / 16, new Size(281, 499));
+ }
+
+ private void forEveryTestCaseCheck(Map<Float, Size> expectedSizes,
+ Function<Float, Size> callback) {
+ for (Map.Entry<Float, Size> expectedSizesEntry : expectedSizes.entrySet()) {
+ float aspectRatio = expectedSizesEntry.getKey();
+ Size expectedSize = expectedSizesEntry.getValue();
+
+ Assert.assertEquals(expectedSize, callback.apply(aspectRatio));
+ }
+ }
+
+ @Before
+ public void setUp() {
+ initExpectedSizes();
+ setUpStaticSystemPropertiesSession();
+
+ when(mResources.getDimensionPixelSize(anyInt())).thenReturn(DEFAULT_MIN_EDGE_SIZE);
+ when(mResources.getFloat(anyInt())).thenReturn(OPTIMIZED_ASPECT_RATIO);
+ when(mResources.getString(anyInt())).thenReturn("0x0");
+ when(mResources.getDisplayMetrics())
+ .thenReturn(getContext().getResources().getDisplayMetrics());
+
+ // set up the mock context for spec handler specifically
+ when(mContext.getResources()).thenReturn(mResources);
+
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+
+ // no overridden min edge size by default
+ mPipSizeSpecHandler.setOverrideMinSize(null);
+
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = DISPLAY_EDGE_SIZE;
+ displayInfo.logicalHeight = DISPLAY_EDGE_SIZE;
+
+ // use the parent context (not the mocked one) to obtain the display layout
+ // this is done to avoid unnecessary mocking while allowing for custom display dimensions
+ DisplayLayout displayLayout = new DisplayLayout(displayInfo, getContext().getResources(),
+ false, false);
+ mPipSizeSpecHandler.setDisplayLayout(displayLayout);
+ }
+
+ @After
+ public void cleanUp() {
+ sStaticMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testGetMaxSize() {
+ forEveryTestCaseCheck(sExpectedMaxSizes,
+ (aspectRatio) -> mPipSizeSpecHandler.getMaxSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetDefaultSize() {
+ forEveryTestCaseCheck(sExpectedDefaultSizes,
+ (aspectRatio) -> mPipSizeSpecHandler.getDefaultSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetMinSize() {
+ forEveryTestCaseCheck(sExpectedMinSizes,
+ (aspectRatio) -> mPipSizeSpecHandler.getMinSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetSizeForAspectRatio_noOverrideMinSize() {
+ // an initial size with 16:9 aspect ratio
+ Size initSize = new Size(600, 337);
+
+ Size expectedSize = new Size(337, 599);
+ Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16);
+
+ Assert.assertEquals(expectedSize, actualSize);
+ }
+
+ @Test
+ public void testGetSizeForAspectRatio_withOverrideMinSize() {
+ // an initial size with a 1:1 aspect ratio
+ mPipSizeSpecHandler.setOverrideMinSize(new Size(OVERRIDE_MIN_EDGE_SIZE,
+ OVERRIDE_MIN_EDGE_SIZE));
+ // make sure initial size is same as override min size
+ Size initSize = mPipSizeSpecHandler.getOverrideMinSize();
+
+ Size expectedSize = new Size(40, 71);
+ Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16);
+
+ Assert.assertEquals(expectedSize, actualSize);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 8ad2932..5c4863f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -25,6 +25,7 @@
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Size;
import androidx.test.filters.SmallTest;
@@ -90,6 +91,7 @@
private PipSnapAlgorithm mPipSnapAlgorithm;
private PipMotionHelper mMotionHelper;
private PipResizeGestureHandler mPipResizeGestureHandler;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
private DisplayLayout mDisplayLayout;
private Rect mInsetBounds;
@@ -103,16 +105,17 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
- new PipKeepClearAlgorithmInterface() {});
+ new PipKeepClearAlgorithmInterface() {}, mPipSizeSpecHandler);
PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
mPipTouchHandler = new PipTouchHandler(mContext, mShellInit, mPhonePipMenuController,
- mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, pipMotionHelper,
- mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
+ mPipBoundsAlgorithm, mPipBoundsState, mPipSizeSpecHandler, mPipTaskOrganizer,
+ pipMotionHelper, mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
// We aren't actually using ShellInit, so just call init directly
mPipTouchHandler.onInit();
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
@@ -122,6 +125,7 @@
mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
mPipBoundsState.setDisplayLayout(mDisplayLayout);
+ mPipSizeSpecHandler.setDisplayLayout(mDisplayLayout);
mInsetBounds = new Rect(mPipBoundsState.getDisplayBounds().left + INSET,
mPipBoundsState.getDisplayBounds().top + INSET,
mPipBoundsState.getDisplayBounds().right - INSET,
@@ -154,13 +158,17 @@
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+ // getting the expected min and max size
+ float aspectRatio = (float) mPipBounds.width() / mPipBounds.height();
+ Size expectedMinSize = mPipSizeSpecHandler.getMinSize(aspectRatio);
+ Size expectedMaxSize = mPipSizeSpecHandler.getMaxSize(aspectRatio);
+
assertEquals(expectedMovementBounds, mPipBoundsState.getNormalMovementBounds());
verify(mPipResizeGestureHandler, times(1))
- .updateMinSize(mPipBounds.width(), mPipBounds.height());
+ .updateMinSize(expectedMinSize.getWidth(), expectedMinSize.getHeight());
verify(mPipResizeGestureHandler, times(1))
- .updateMaxSize(shorterLength - 2 * mInsetBounds.left,
- shorterLength - 2 * mInsetBounds.left);
+ .updateMaxSize(expectedMaxSize.getWidth(), expectedMaxSize.getHeight());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 51f86b8..30096cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -27,6 +27,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +46,7 @@
private TvPipBoundsState mTvPipBoundsState;
private TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
@Before
public void setUp() {
@@ -52,9 +54,10 @@
return;
}
MockitoAnnotations.initMocks(this);
- mTvPipBoundsState = new TvPipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mTvPipBoundsState = new TvPipBoundsState(mContext, mPipSizeSpecHandler);
mTvPipBoundsAlgorithm = new TvPipBoundsAlgorithm(mContext, mTvPipBoundsState,
- mMockPipSnapAlgorithm);
+ mMockPipSnapAlgorithm, mPipSizeSpecHandler);
setRTL(false);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
new file mode 100644
index 0000000..8f84008
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.os.SystemClock
+import android.testing.AndroidTestingRunner
+import android.view.MotionEvent
+import android.view.InputDevice
+import androidx.test.filters.SmallTest
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/**
+ * Tests for [DragDetector].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DragDetectorTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DragDetectorTest {
+ private val motionEvents = mutableListOf<MotionEvent>()
+
+ @Mock
+ private lateinit var eventHandler: DragDetector.MotionEventHandler
+
+ private lateinit var dragDetector: DragDetector
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
+
+ dragDetector = DragDetector(eventHandler)
+ dragDetector.setTouchSlop(SLOP)
+ }
+
+ @After
+ fun tearDown() {
+ motionEvents.forEach {
+ it.recycle()
+ }
+ motionEvents.clear()
+ }
+
+ @Test
+ fun testNoMove_passesDownAndUp() {
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+ }
+
+ @Test
+ fun testMoveInSlop_touch_passesDownAndUp() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ val newX = X + SLOP - 1
+ assertFalse(
+ dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+ verify(eventHandler, never()).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+ }
+
+ @Test
+ fun testMoveInSlop_mouse_passesDownMoveAndUp() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ it.action == MotionEvent.ACTION_DOWN
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+
+ val newX = X + SLOP - 1
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+ }
+
+ @Test
+ fun testMoveBeyondSlop_passesDownMoveAndUp() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ it.action == MotionEvent.ACTION_DOWN
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ val newX = X + SLOP + 1
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+ }
+
+ @Test
+ fun testPassesHoverEnter() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ it.action == MotionEvent.ACTION_HOVER_ENTER
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
+ })
+ }
+
+ @Test
+ fun testPassesHoverMove() {
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
+ })
+ }
+
+ @Test
+ fun testPassesHoverExit() {
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
+ })
+ }
+
+ private fun createMotionEvent(action: Int, x: Float = X, y: Float = Y, isTouch: Boolean = true):
+ MotionEvent {
+ val time = SystemClock.uptimeMillis()
+ val ev = MotionEvent.obtain(time, time, action, x, y, 0)
+ ev.source = if (isTouch) InputDevice.SOURCE_TOUCHSCREEN else InputDevice.SOURCE_MOUSE
+ motionEvents.add(ev)
+ return ev
+ }
+
+ companion object {
+ private const val SLOP = 10
+ private const val X = 123f
+ private const val Y = 234f
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index ac10ddb..f185a8a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -1,6 +1,7 @@
package com.android.wm.shell.windowdecor
import android.app.ActivityManager
+import android.app.WindowConfiguration
import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
@@ -10,6 +11,7 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
import org.junit.Before
import org.junit.Test
@@ -63,8 +65,92 @@
}
@Test
+ fun testDragResize_notMove_skipsTransactionOnEnd() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragPositioningMove(
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragPositioningMove(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat()
+ )
+ val rectAfterMove = Rect(STARTING_BOUNDS)
+ rectAfterMove.right += 10
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterMove
+ }
+ })
+
+ taskPositioner.onDragPositioningEnd(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+ val rectAfterEnd = Rect(rectAfterMove)
+ rectAfterEnd.top += 10
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterEnd
+ }
+ })
+ }
+
+ @Test
fun testDragResize_move_skipsDragResizingFlag() {
- taskPositioner.onDragResizeStart(
+ taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // Move
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
@@ -73,12 +159,12 @@
// Move the task 10px to the right.
val newX = STARTING_BOUNDS.left.toFloat() + 10
val newY = STARTING_BOUNDS.top.toFloat()
- taskPositioner.onDragResizeMove(
+ taskPositioner.onDragPositioningMove(
newX,
newY
)
- taskPositioner.onDragResizeEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(newX, newY)
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -91,7 +177,7 @@
@Test
fun testDragResize_resize_setsDragResizingFlag() {
- taskPositioner.onDragResizeStart(
+ taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
@@ -100,12 +186,12 @@
// Resize the task by 10px to the right.
val newX = STARTING_BOUNDS.right.toFloat() + 10
val newY = STARTING_BOUNDS.top.toFloat()
- taskPositioner.onDragResizeMove(
+ taskPositioner.onDragPositioningMove(
newX,
newY
)
- taskPositioner.onDragResizeEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index ec4f17f..2f5263c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -107,6 +107,7 @@
private SurfaceControl.Transaction mMockSurfaceControlFinishT;
private SurfaceControl.Transaction mMockSurfaceControlAddWindowT;
private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
+ private int mCaptionMenuWidthId;
@Before
public void setUp() {
@@ -116,8 +117,7 @@
mRelayoutParams.mLayoutResId = 0;
mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
- // Caption should have fixed width except in testLayoutResultCalculation_fullWidthCaption()
- mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
+ mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width;
mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
@@ -240,7 +240,7 @@
verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
verify(captionContainerSurfaceBuilder).setContainerLayer();
verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
- verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 432, 64);
+ verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
@@ -248,7 +248,7 @@
verify(mMockSurfaceControlViewHost)
.setView(same(mMockView),
argThat(lp -> lp.height == 64
- && lp.width == 432
+ && lp.width == 300
&& (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
if (ViewRootImpl.CAPTION_ON_SHELL) {
verify(mMockView).setTaskFocusState(true);
@@ -484,7 +484,6 @@
final SurfaceControl taskSurface = mock(SurfaceControl.class);
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
- mRelayoutParams.mCaptionWidthId = Resources.ID_NULL;
windowDecor.relayout(taskInfo);
verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
@@ -558,7 +557,7 @@
final Resources resources = mDecorWindowContext.getResources();
int x = mRelayoutParams.mCaptionX;
int y = mRelayoutParams.mCaptionY;
- int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
+ int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId);
int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
String name = "Test Window";
WindowDecoration.AdditionalWindow additionalWindow =
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index b1f327c..28bda72 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -55,6 +55,7 @@
host_supported: true,
srcs: [
"ApkAssets.cpp",
+ "ApkParsing.cpp",
"Asset.cpp",
"AssetDir.cpp",
"AssetManager.cpp",
@@ -160,6 +161,7 @@
// Actual tests.
"tests/ApkAssets_test.cpp",
+ "tests/ApkParsing_test.cpp",
"tests/AppAsLib_test.cpp",
"tests/Asset_test.cpp",
"tests/AssetManager2_test.cpp",
diff --git a/libs/androidfw/ApkParsing.cpp b/libs/androidfw/ApkParsing.cpp
new file mode 100644
index 0000000..cf4fbb9
--- /dev/null
+++ b/libs/androidfw/ApkParsing.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "androidfw/ApkParsing.h"
+#include <algorithm>
+#include <array>
+#include <stdlib.h>
+#include <string_view>
+#include <sys/types.h>
+
+const std::string_view APK_LIB = "lib/";
+const size_t APK_LIB_LEN = APK_LIB.size();
+
+const std::string_view LIB_PREFIX = "/lib";
+const size_t LIB_PREFIX_LEN = LIB_PREFIX.size();
+
+const std::string_view LIB_SUFFIX = ".so";
+const size_t LIB_SUFFIX_LEN = LIB_SUFFIX.size();
+
+static const std::array<std::string_view, 2> abis = {"arm64-v8a", "x86_64"};
+
+namespace android::util {
+const char* ValidLibraryPathLastSlash(const char* fileName, bool suppress64Bit, bool debuggable) {
+ // Make sure the filename is at least to the minimum library name size.
+ const size_t fileNameLen = strlen(fileName);
+ static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
+ if (fileNameLen < minLength) {
+ return nullptr;
+ }
+
+ const char* lastSlash = strrchr(fileName, '/');
+ if (!lastSlash) {
+ return nullptr;
+ }
+
+ // Skip directories.
+ if (*(lastSlash + 1) == 0) {
+ return nullptr;
+ }
+
+ // Make sure the filename is safe.
+ if (!isFilenameSafe(lastSlash + 1)) {
+ return nullptr;
+ }
+
+ // Make sure there aren't subdirectories
+ const char* abiOffset = fileName + APK_LIB_LEN;
+ const size_t abiSize = lastSlash - abiOffset;
+ if (memchr(abiOffset, '/', abiSize)) {
+ return nullptr;
+ }
+
+ if (!debuggable) {
+ // Make sure the filename starts with lib and ends with ".so".
+ if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX.data(), LIB_SUFFIX_LEN) != 0
+ || strncmp(lastSlash, LIB_PREFIX.data(), LIB_PREFIX_LEN) != 0) {
+ return nullptr;
+ }
+ }
+
+ // Don't include 64 bit versions if they are suppressed
+ if (suppress64Bit && std::find(abis.begin(), abis.end(), std::string_view(
+ fileName + APK_LIB_LEN, lastSlash - fileName - APK_LIB_LEN)) != abis.end()) {
+ return nullptr;
+ }
+
+ return lastSlash;
+}
+
+bool isFilenameSafe(const char* filename) {
+ off_t offset = 0;
+ for (;;) {
+ switch (*(filename + offset)) {
+ case 0:
+ // Null.
+ // If we've reached the end, all the other characters are good.
+ return true;
+
+ case 'A' ... 'Z':
+ case 'a' ... 'z':
+ case '0' ... '9':
+ case '+':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ case '=':
+ case '_':
+ offset++;
+ break;
+
+ default:
+ // We found something that is not good.
+ return false;
+ }
+ }
+ // Should not reach here.
+}
+}
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/ApkParsing.h b/libs/androidfw/include/androidfw/ApkParsing.h
new file mode 100644
index 0000000..194eaae
--- /dev/null
+++ b/libs/androidfw/include/androidfw/ApkParsing.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <string_view>
+#include <sys/types.h>
+
+extern const std::string_view APK_LIB;
+extern const size_t APK_LIB_LEN;
+
+namespace android::util {
+// Checks if filename is a valid library path and returns a pointer to the last slash in the path
+// if it is, nullptr otherwise
+const char* ValidLibraryPathLastSlash(const char* filename, bool suppress64Bit, bool debuggable);
+
+// Equivalent to android.os.FileUtils.isFilenameSafe
+bool isFilenameSafe(const char* filename);
+}
diff --git a/libs/androidfw/tests/ApkParsing_test.cpp b/libs/androidfw/tests/ApkParsing_test.cpp
new file mode 100644
index 0000000..4aab0a1
--- /dev/null
+++ b/libs/androidfw/tests/ApkParsing_test.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "androidfw/ApkParsing.h"
+
+#include "android-base/test_utils.h"
+
+#include "TestHelpers.h"
+
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+TEST(ApkParsingTest, ValidArm64Path) {
+ const char* path = "lib/arm64-v8a/library.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, NotNull());
+ ASSERT_THAT(lastSlash, Eq(path + 13));
+}
+
+TEST(ApkParsingTest, ValidArm64PathButSuppressed) {
+ const char* path = "lib/arm64-v8a/library.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, true, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
+
+TEST(ApkParsingTest, ValidArm32Path) {
+ const char* path = "lib/armeabi-v7a/library.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, NotNull());
+ ASSERT_THAT(lastSlash, Eq(path + 15));
+}
+
+TEST(ApkParsingTest, InvalidMustStartWithLib) {
+ const char* path = "lib/arm64-v8a/random.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
+
+TEST(ApkParsingTest, InvalidMustEndInSo) {
+ const char* path = "lib/arm64-v8a/library.txt";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
+
+TEST(ApkParsingTest, InvalidCharacter) {
+ const char* path = "lib/arm64-v8a/lib#.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
+
+TEST(ApkParsingTest, InvalidSubdirectories) {
+ const char* path = "lib/arm64-v8a/anything/library.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
+}
\ No newline at end of file
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 8d4bda2..dd6c2bc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -347,6 +347,7 @@
"jni/CreateJavaOutputStreamAdaptor.cpp",
"jni/FontFamily.cpp",
"jni/FontUtils.cpp",
+ "jni/Gainmap.cpp",
"jni/Graphics.cpp",
"jni/ImageDecoder.cpp",
"jni/Interpolator.cpp",
diff --git a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl b/libs/hwui/Gainmap.h
similarity index 61%
copy from core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
copy to libs/hwui/Gainmap.h
index b30a12a..765f98a 100644
--- a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
+++ b/libs/hwui/Gainmap.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-package android.credentials;
+#pragma once
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IUnregisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+#include <SkGainmapInfo.h>
+#include <SkImage.h>
+#include <hwui/Bitmap.h>
+#include <utils/LightRefBase.h>
+
+namespace android::uirenderer {
+
+class Gainmap : public LightRefBase<Gainmap> {
+public:
+ SkGainmapInfo info;
+ sk_sp<Bitmap> bitmap;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index f57d80c..c509ed4 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -55,6 +55,7 @@
extern int register_android_graphics_ColorSpace(JNIEnv* env);
extern int register_android_graphics_DrawFilter(JNIEnv* env);
extern int register_android_graphics_FontFamily(JNIEnv* env);
+extern int register_android_graphics_Gainmap(JNIEnv* env);
extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env);
extern int register_android_graphics_Matrix(JNIEnv* env);
extern int register_android_graphics_Paint(JNIEnv* env);
@@ -114,6 +115,7 @@
REG_JNI(register_android_graphics_ColorFilter),
REG_JNI(register_android_graphics_DrawFilter),
REG_JNI(register_android_graphics_FontFamily),
+ REG_JNI(register_android_graphics_Gainmap),
REG_JNI(register_android_graphics_HardwareRendererObserver),
REG_JNI(register_android_graphics_ImageDecoder),
REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index feafc23..0a755f0 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -34,6 +34,7 @@
#include <binder/IServiceManager.h>
#endif
+#include <Gainmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkEncodedImageFormat.h>
@@ -44,6 +45,7 @@
#include <SkRect.h>
#include <SkStream.h>
#include <SkWebpEncoder.h>
+
#include <limits>
namespace android {
@@ -494,4 +496,14 @@
return SkEncodeImage(stream, bitmap, fm, quality);
}
+
+sp<uirenderer::Gainmap> Bitmap::gainmap() const {
+ LOG_ALWAYS_FATAL_IF(!hasGainmap(), "Bitmap doesn't have a gainmap");
+ return mGainmap;
+}
+
+void Bitmap::setGainmap(sp<uirenderer::Gainmap>&& gainmap) {
+ mGainmap = std::move(gainmap);
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 133f1fe..912d311 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -23,6 +23,10 @@
#include <SkPixelRef.h>
#include <SkRefCnt.h>
#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
+#include <optional>
+
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
#include <android/hardware_buffer.h>
#endif
@@ -47,6 +51,7 @@
};
namespace uirenderer {
+class Gainmap;
namespace renderthread {
class RenderThread;
}
@@ -119,6 +124,11 @@
void getBounds(SkRect* bounds) const;
bool isHardware() const { return mPixelStorageType == PixelStorageType::Hardware; }
+ bool hasGainmap() const { return mGainmap.get() != nullptr; }
+
+ sp<uirenderer::Gainmap> gainmap() const;
+
+ void setGainmap(sp<uirenderer::Gainmap>&& gainmap);
PixelStorageType pixelStorageType() const { return mPixelStorageType; }
@@ -193,6 +203,8 @@
bool mHasHardwareMipMap = false;
+ sp<uirenderer::Gainmap> mGainmap;
+
union {
struct {
SkPixelRef* pixelRef;
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index dd68f82..b1abe85 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -16,15 +16,20 @@
#include "ImageDecoder.h"
-#include <hwui/Bitmap.h>
-#include <log/log.h>
-
+#include <Gainmap.h>
#include <SkAndroidCodec.h>
#include <SkBitmap.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkEncodedOrigin.h>
+#include <SkGainmapInfo.h>
#include <SkPaint.h>
+#include <SkStream.h>
+#include <hwui/Bitmap.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <memory>
#undef LOG_TAG
#define LOG_TAG "ImageDecoder"
@@ -195,7 +200,7 @@
}
bool ImageDecoder::swapWidthHeight() const {
- return SkEncodedOriginSwapsWidthHeight(mCodec->codec()->getOrigin());
+ return SkEncodedOriginSwapsWidthHeight(getOrigin());
}
int ImageDecoder::width() const {
@@ -316,7 +321,7 @@
info.fFrameRect = SkIRect::MakeSize(dims);
}
- if (auto origin = mCodec->codec()->getOrigin(); origin != kDefault_SkEncodedOrigin) {
+ if (auto origin = getOrigin(); origin != kDefault_SkEncodedOrigin) {
if (SkEncodedOriginSwapsWidthHeight(origin)) {
dims = swapped(dims);
}
@@ -400,7 +405,7 @@
// FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
SkBitmap tmp;
const bool scale = mDecodeSize != mTargetSize;
- const auto origin = mCodec->codec()->getOrigin();
+ const auto origin = getOrigin();
const bool handleOrigin = origin != kDefault_SkEncodedOrigin;
SkMatrix outputMatrix;
if (scale || handleOrigin || mCropRect) {
@@ -455,12 +460,15 @@
mOptions.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
}
+ ATRACE_BEGIN("getAndroidPixels");
auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &mOptions);
+ ATRACE_END();
// The next call to decode() may not provide zero initialized memory.
mOptions.fZeroInitialized = SkCodec::kNo_ZeroInitialized;
if (scale || handleOrigin || mCropRect) {
+ ATRACE_NAME("Handling scale/origin/crop");
SkBitmap scaledBm;
if (!scaledBm.installPixels(outputInfo, pixels, rowBytes)) {
return SkCodec::kInternalError;
@@ -478,3 +486,71 @@
return result;
}
+SkCodec::Result ImageDecoder::extractGainmap(Bitmap* destination) {
+ ATRACE_CALL();
+ SkGainmapInfo gainmapInfo;
+ std::unique_ptr<SkStream> gainmapStream;
+ {
+ ATRACE_NAME("getAndroidGainmap");
+ if (!mCodec->getAndroidGainmap(&gainmapInfo, &gainmapStream)) {
+ return SkCodec::kSuccess;
+ }
+ }
+ auto gainmapCodec = SkAndroidCodec::MakeFromStream(std::move(gainmapStream));
+ if (!gainmapCodec) {
+ ALOGW("Failed to create codec for gainmap stream");
+ return SkCodec::kInvalidInput;
+ }
+ ImageDecoder decoder{std::move(gainmapCodec)};
+ // Gainmap inherits the origin of the containing image
+ decoder.mOverrideOrigin.emplace(getOrigin());
+
+ const bool isScaled = width() != mTargetSize.width() || height() != mTargetSize.height();
+
+ if (isScaled) {
+ float scaleX = (float)mTargetSize.width() / width();
+ float scaleY = (float)mTargetSize.height() / height();
+ decoder.setTargetSize(decoder.width() * scaleX, decoder.height() * scaleY);
+ }
+
+ if (mCropRect) {
+ float sX = decoder.mTargetSize.width() / (float)mTargetSize.width();
+ float sY = decoder.mTargetSize.height() / (float)mTargetSize.height();
+ SkIRect crop = *mCropRect;
+ // TODO: Tweak rounding?
+ crop.fLeft *= sX;
+ crop.fRight *= sX;
+ crop.fTop *= sY;
+ crop.fBottom *= sY;
+ decoder.setCropRect(&crop);
+ }
+
+ SkImageInfo bitmapInfo = decoder.getOutputInfo();
+
+ SkBitmap bm;
+ if (!bm.setInfo(bitmapInfo)) {
+ ALOGE("Failed to setInfo properly");
+ return SkCodec::kInternalError;
+ }
+
+ // TODO: We don't currently parcel the gainmap, but if we should then also support
+ // the shared allocator
+ sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bm);
+ if (!nativeBitmap) {
+ ALOGE("OOM allocating Bitmap with dimensions %i x %i", bitmapInfo.width(),
+ bitmapInfo.height());
+ return SkCodec::kInternalError;
+ }
+
+ SkCodec::Result result = decoder.decode(bm.getPixels(), bm.rowBytes());
+ bm.setImmutable();
+
+ if (result == SkCodec::kSuccess) {
+ auto gainmap = sp<uirenderer::Gainmap>::make();
+ gainmap->info = gainmapInfo;
+ gainmap->bitmap = std::move(nativeBitmap);
+ destination->setGainmap(std::move(gainmap));
+ }
+
+ return result;
+}
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index b6d73b3..97573e1 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -79,6 +79,8 @@
// Set whether the ImageDecoder should handle RestorePrevious frames.
void setHandleRestorePrevious(bool handle);
+ SkCodec::Result extractGainmap(Bitmap* destination);
+
private:
// State machine for keeping track of how to handle RestorePrevious (RP)
// frames in decode().
@@ -115,6 +117,7 @@
RestoreState mRestoreState;
sk_sp<Bitmap> mRestoreFrame;
std::optional<SkIRect> mCropRect;
+ std::optional<SkEncodedOrigin> mOverrideOrigin;
ImageDecoder(const ImageDecoder&) = delete;
ImageDecoder& operator=(const ImageDecoder&) = delete;
@@ -124,6 +127,10 @@
bool swapWidthHeight() const;
// Store/restore a frame if necessary. Returns false on error.
bool handleRestorePrevious(const SkImageInfo&, void* pixels, size_t rowBytes);
+
+ SkEncodedOrigin getOrigin() const {
+ return mOverrideOrigin.has_value() ? *mOverrideOrigin : mCodec->codec()->getOrigin();
+ }
};
} // namespace android
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
old mode 100755
new mode 100644
index 540abec..10c287d
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -6,6 +6,7 @@
#include <hwui/Paint.h>
#include "CreateJavaOutputStreamAdaptor.h"
+#include "Gainmap.h"
#include "GraphicsJNI.h"
#include "HardwareBufferHelpers.h"
#include "SkBitmap.h"
@@ -47,6 +48,8 @@
namespace android {
+jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap);
+
class BitmapWrapper {
public:
explicit BitmapWrapper(Bitmap* bitmap)
@@ -422,6 +425,11 @@
return ret;
}
+static jint Bitmap_getAshmemFd(JNIEnv* env, jobject, jlong bitmapHandle) {
+ LocalScopedBitmap bitmap(bitmapHandle);
+ return (bitmap.valid()) ? bitmap->bitmap().getAshmemFd() : -1;
+}
+
static void Bitmap_destruct(BitmapWrapper* bitmap) {
delete bitmap;
}
@@ -1246,67 +1254,77 @@
return bitmapHolder->bitmap().setImmutable();
}
+static jboolean Bitmap_hasGainmap(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) {
+ LocalScopedBitmap bitmapHolder(bitmapHandle);
+ if (!bitmapHolder.valid()) return false;
+
+ return bitmapHolder->bitmap().hasGainmap();
+}
+
+static jobject Bitmap_extractGainmap(JNIEnv* env, jobject, jlong bitmapHandle) {
+ LocalScopedBitmap bitmapHolder(bitmapHandle);
+ if (!bitmapHolder.valid()) return nullptr;
+ if (!bitmapHolder->bitmap().hasGainmap()) return nullptr;
+
+ return Gainmap_extractFromBitmap(env, bitmapHolder->bitmap());
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gBitmapMethods[] = {
- { "nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;",
- (void*)Bitmap_creator },
- { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;",
- (void*)Bitmap_copy },
- { "nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;",
- (void*)Bitmap_copyAshmem },
- { "nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;",
- (void*)Bitmap_copyAshmemConfig },
- { "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
- { "nativeRecycle", "(J)V", (void*)Bitmap_recycle },
- { "nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure },
- { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
- (void*)Bitmap_compress },
- { "nativeErase", "(JI)V", (void*)Bitmap_erase },
- { "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong },
- { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
- { "nativeConfig", "(J)I", (void*)Bitmap_config },
- { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
- { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied},
- { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha},
- { "nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied},
- { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap },
- { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap },
- { "nativeCreateFromParcel",
- "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
- (void*)Bitmap_createFromParcel },
- { "nativeWriteToParcel", "(JILandroid/os/Parcel;)Z",
- (void*)Bitmap_writeToParcel },
- { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;",
- (void*)Bitmap_extractAlpha },
- { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId },
- { "nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel },
- { "nativeGetColor", "(JII)J", (void*)Bitmap_getColor },
- { "nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels },
- { "nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel },
- { "nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels },
- { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
- (void*)Bitmap_copyPixelsToBuffer },
- { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
- (void*)Bitmap_copyPixelsFromBuffer },
- { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
- { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw },
- { "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
- { "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
- (void*)Bitmap_copyPreserveInternalConfig },
- { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
- (void*) Bitmap_wrapHardwareBufferBitmap },
- { "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;",
- (void*) Bitmap_getHardwareBuffer },
- { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace },
- { "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace },
- { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB },
- { "nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear},
- { "nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable},
+ {"nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;", (void*)Bitmap_creator},
+ {"nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", (void*)Bitmap_copy},
+ {"nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;", (void*)Bitmap_copyAshmem},
+ {"nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;", (void*)Bitmap_copyAshmemConfig},
+ {"nativeGetAshmemFD", "(J)I", (void*)Bitmap_getAshmemFd},
+ {"nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer},
+ {"nativeRecycle", "(J)V", (void*)Bitmap_recycle},
+ {"nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure},
+ {"nativeCompress", "(JIILjava/io/OutputStream;[B)Z", (void*)Bitmap_compress},
+ {"nativeErase", "(JI)V", (void*)Bitmap_erase},
+ {"nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong},
+ {"nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes},
+ {"nativeConfig", "(J)I", (void*)Bitmap_config},
+ {"nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha},
+ {"nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied},
+ {"nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha},
+ {"nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied},
+ {"nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap},
+ {"nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap},
+ {"nativeCreateFromParcel", "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_createFromParcel},
+ {"nativeWriteToParcel", "(JILandroid/os/Parcel;)Z", (void*)Bitmap_writeToParcel},
+ {"nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", (void*)Bitmap_extractAlpha},
+ {"nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId},
+ {"nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel},
+ {"nativeGetColor", "(JII)J", (void*)Bitmap_getColor},
+ {"nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels},
+ {"nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel},
+ {"nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels},
+ {"nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsToBuffer},
+ {"nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsFromBuffer},
+ {"nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs},
+ {"nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw},
+ {"nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount},
+ {"nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_copyPreserveInternalConfig},
+ {"nativeWrapHardwareBufferBitmap",
+ "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_wrapHardwareBufferBitmap},
+ {"nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;",
+ (void*)Bitmap_getHardwareBuffer},
+ {"nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;",
+ (void*)Bitmap_computeColorSpace},
+ {"nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace},
+ {"nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB},
+ {"nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear},
+ {"nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable},
+ {"nativeExtractGainmap", "(J)Landroid/graphics/Gainmap;", (void*)Bitmap_extractGainmap},
- // ------------ @CriticalNative ----------------
- { "nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable},
- { "nativeIsBackedByAshmem", "(J)Z", (void*)Bitmap_isBackedByAshmem}
+ // ------------ @CriticalNative ----------------
+ {"nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable},
+ {"nativeIsBackedByAshmem", "(J)Z", (void*)Bitmap_isBackedByAshmem},
+ {"nativeHasGainmap", "(J)Z", (void*)Bitmap_hasGainmap},
};
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 2f3e9bf..0d5995a 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -22,7 +22,6 @@
#include "SkSize.h"
#include "SkStream.h"
#include "SkString.h"
-#include "SkUtils.h"
#include "Utils.h"
#include <HardwareBitmapUploader.h>
diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp
new file mode 100644
index 0000000..f2efbc7
--- /dev/null
+++ b/libs/hwui/jni/Gainmap.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Gainmap.h>
+
+#include "Bitmap.h"
+#include "GraphicsJNI.h"
+#include "graphics_jni_helpers.h"
+
+namespace android {
+
+static jclass gGainmap_class;
+static jmethodID gGainmap_constructorMethodID;
+
+using namespace uirenderer;
+
+static Gainmap* fromJava(jlong gainmap) {
+ return reinterpret_cast<Gainmap*>(gainmap);
+}
+
+static int getCreateFlags(const sk_sp<Bitmap>& bitmap) {
+ int flags = 0;
+ if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
+ flags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
+ }
+ if (!bitmap->isImmutable()) {
+ flags |= android::bitmap::kBitmapCreateFlag_Mutable;
+ }
+ return flags;
+}
+
+jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap) {
+ auto gainmap = bitmap.gainmap();
+ jobject jGainmapImage;
+ size_t allocationSize;
+
+ {
+ // Scope to guard the release of nativeBitmap
+ auto nativeBitmap = gainmap->bitmap;
+ const int createFlags = getCreateFlags(nativeBitmap);
+ allocationSize = nativeBitmap->getAllocationByteCount();
+ jGainmapImage = bitmap::createBitmap(env, nativeBitmap.release(), createFlags);
+ }
+
+ // Grab a ref for the jobject
+ gainmap->incStrong(0);
+ jobject obj = env->NewObject(gGainmap_class, gGainmap_constructorMethodID, jGainmapImage,
+ gainmap.get(), allocationSize + sizeof(Gainmap), true);
+
+ if (env->ExceptionCheck() != 0) {
+ // sadtrombone
+ gainmap->decStrong(0);
+ ALOGE("*** Uncaught exception returned from Java call!\n");
+ env->ExceptionDescribe();
+ }
+ return obj;
+}
+
+static void Gainmap_destructor(Gainmap* gainmap) {
+ gainmap->decStrong(0);
+}
+
+static jlong Gainmap_getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Gainmap_destructor));
+}
+
+static void Gainmap_setGainmapMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g,
+ jfloat b) {
+ fromJava(gainmapPtr)->info.fLogRatioMax = {r, g, b, 1.f};
+}
+
+static void Gainmap_getGainmapMax(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
+ const auto ratioMax = fromJava(gainmapPtr)->info.fLogRatioMax;
+ jfloat buf[3]{ratioMax.fR, ratioMax.fG, ratioMax.fB};
+ env->SetFloatArrayRegion(components, 0, 3, buf);
+}
+
+static void Gainmap_setHdrRatioMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat max) {
+ fromJava(gainmapPtr)->info.fHdrRatioMax = max;
+}
+
+static jfloat Gainmap_getHdrRatioMax(JNIEnv*, jobject, jlong gainmapPtr) {
+ return fromJava(gainmapPtr)->info.fHdrRatioMax;
+}
+
+static void Gainmap_setHdrRatioMin(JNIEnv*, jobject, jlong gainmapPtr, jfloat min) {
+ fromJava(gainmapPtr)->info.fHdrRatioMin = min;
+}
+
+static jfloat Gainmap_getHdrRatioMin(JNIEnv*, jobject, jlong gainmapPtr) {
+ return fromJava(gainmapPtr)->info.fHdrRatioMin;
+}
+
+static const JNINativeMethod gGainmapMethods[] = {
+ {"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer},
+ {"nSetGainmapMax", "(JFFF)V", (void*)Gainmap_setGainmapMax},
+ {"nGetGainmapMax", "(J[F)V", (void*)Gainmap_getGainmapMax},
+ {"nSetHdrRatioMax", "(JF)V", (void*)Gainmap_setHdrRatioMax},
+ {"nGetHdrRatioMax", "(J)F", (void*)Gainmap_getHdrRatioMax},
+ {"nSetHdrRatioMin", "(JF)V", (void*)Gainmap_setHdrRatioMin},
+ {"nGetHdrRatioMin", "(J)F", (void*)Gainmap_getHdrRatioMin},
+};
+
+int register_android_graphics_Gainmap(JNIEnv* env) {
+ gGainmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Gainmap"));
+ gGainmap_constructorMethodID =
+ GetMethodIDOrDie(env, gGainmap_class, "<init>", "(Landroid/graphics/Bitmap;JIZ)V");
+ return android::RegisterMethodsOrDie(env, "android/graphics/Gainmap", gGainmapMethods,
+ NELEM(gGainmapMethods));
+}
+
+} // namespace android
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index bad710d..add62b1 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -14,20 +14,10 @@
* limitations under the License.
*/
-#include "Bitmap.h"
-#include "BitmapFactory.h"
-#include "ByteBufferStreamAdaptor.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-#include "GraphicsJNI.h"
#include "ImageDecoder.h"
-#include "NinePatchPeeker.h"
-#include "Utils.h"
-
-#include <hwui/Bitmap.h>
-#include <hwui/ImageDecoder.h>
-#include <HardwareBitmapUploader.h>
#include <FrontBufferedStream.h>
+#include <HardwareBitmapUploader.h>
#include <SkAndroidCodec.h>
#include <SkBitmap.h>
#include <SkColorSpace.h>
@@ -35,11 +25,22 @@
#include <SkRect.h>
#include <SkStream.h>
#include <SkString.h>
-
#include <androidfw/Asset.h>
#include <fcntl.h>
+#include <gui/TraceUtils.h>
+#include <hwui/Bitmap.h>
+#include <hwui/ImageDecoder.h>
#include <sys/stat.h>
+#include "Bitmap.h"
+#include "BitmapFactory.h"
+#include "ByteBufferStreamAdaptor.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "Gainmap.h"
+#include "GraphicsJNI.h"
+#include "NinePatchPeeker.h"
+#include "Utils.h"
+
using namespace android;
static jclass gImageDecoder_class;
@@ -246,6 +247,7 @@
jboolean requireUnpremul, jboolean preferRamOverQuality,
jboolean asAlphaMask, jlong colorSpaceHandle,
jboolean extended) {
+ ATRACE_CALL();
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
if (!decoder->setTargetSize(targetWidth, targetHeight)) {
doThrowISE(env, "Could not scale to target size!");
@@ -336,10 +338,21 @@
return nullptr;
}
+ ATRACE_FORMAT("Decoding %dx%d bitmap", bitmapInfo.width(), bitmapInfo.height());
SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes());
jthrowable jexception = get_and_clear_exception(env);
- int onPartialImageError = jexception ? kSourceException
- : 0; // No error.
+ int onPartialImageError = jexception ? kSourceException : 0; // No error.
+
+ // Only attempt to extract the gainmap if we're not post-processing, as we can't automatically
+ // mimic that to the gainmap and expect it to be meaningful. And also don't extract the gainmap
+ // if we're prioritizing RAM over quality, since the gainmap improves quality at the
+ // cost of RAM
+ if (result == SkCodec::kSuccess && !jpostProcess && !preferRamOverQuality) {
+ // The gainmap costs RAM to improve quality, so skip this if we're prioritizing RAM instead
+ result = decoder->extractGainmap(nativeBitmap.get());
+ jexception = get_and_clear_exception(env);
+ }
+
switch (result) {
case SkCodec::kSuccess:
// Ignore the exception, since the decode was successful anyway.
@@ -450,6 +463,10 @@
sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm);
if (hwBitmap) {
hwBitmap->setImmutable();
+ if (nativeBitmap->hasGainmap()) {
+ // TODO: Also convert to a HW gainmap image
+ hwBitmap->setGainmap(nativeBitmap->gainmap());
+ }
return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
ninePatchChunk, ninePatchInsets);
}
diff --git a/libs/hwui/jni/Movie.cpp b/libs/hwui/jni/Movie.cpp
index bb8c99a..7dfd874 100644
--- a/libs/hwui/jni/Movie.cpp
+++ b/libs/hwui/jni/Movie.cpp
@@ -3,8 +3,8 @@
#include "GraphicsJNI.h"
#include <nativehelper/ScopedLocalRef.h>
#include "Movie.h"
+#include "SkRefCnt.h"
#include "SkStream.h"
-#include "SkUtils.h"
#include "Utils.h"
#include <androidfw/Asset.h>
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index b563627..13357fa 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -37,7 +37,6 @@
#include "SkShader.h"
#include "SkBlendMode.h"
#include "unicode/uloc.h"
-#include "unicode/ushape.h"
#include "utils/Blur.h"
#include <hwui/BlurDrawLooper.h>
diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp
index 106c6db..9f5a214 100644
--- a/libs/hwui/jni/Utils.cpp
+++ b/libs/hwui/jni/Utils.cpp
@@ -15,8 +15,9 @@
*/
#include "Utils.h"
-#include "SkUtils.h"
#include "SkData.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
#include <inttypes.h>
#include <log/log.h>
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 1c76884..23611ef 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -19,7 +19,6 @@
#include <GrContextOptions.h>
#include <SkExecutor.h>
#include <SkGraphics.h>
-#include <SkMathPriv.h>
#include <math.h>
#include <utils/Trace.h>
@@ -47,13 +46,23 @@
setupCacheLimits();
}
+static inline int countLeadingZeros(uint32_t mask) {
+ // __builtin_clz(0) is undefined, so we have to detect that case.
+ return mask ? __builtin_clz(mask) : 32;
+}
+
+// Return the smallest power-of-2 >= n.
+static inline uint32_t nextPowerOfTwo(uint32_t n) {
+ return n ? (1 << (32 - countLeadingZeros(n - 1))) : 1;
+}
+
void CacheManager::setupCacheLimits() {
mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
// This sets the maximum size for a single texture atlas in the GPU font cache. If
// necessary, the cache can allocate additional textures that are counted against the
// total cache limits provided to Skia.
- mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
+ mMaxGpuFontAtlasBytes = nextPowerOfTwo(mMaxSurfaceArea);
// This sets the maximum size of the CPU font cache to be at least the same size as the
// total number of GPU font caches (i.e. 4 separate GPU atlases).
mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f223137..db4d1dc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -194,6 +194,8 @@
ATRACE_CALL();
if (window) {
+ // Ensure the hint session is running here, away from any critical paths
+ mHintSessionWrapper.init();
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
if (enableTimeout) {
@@ -248,6 +250,8 @@
// Order is important when new and old surfaces are the same, because old surface has
// its frame stats disabled automatically.
native_window_enable_frame_timestamps(mNativeSurface->getNativeWindow(), true);
+ native_window_set_scaling_mode(mNativeSurface->getNativeWindow(),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE);
} else {
mRenderThread.removeFrameCallback(this);
mGenerationID++;
@@ -563,7 +567,7 @@
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
native_window_set_frame_timeline_info(
- mNativeSurface->getNativeWindow(), vsyncId, inputEventId,
+ mNativeSurface->getNativeWindow(), frameCompleteNr, vsyncId, inputEventId,
mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime));
}
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index dece548..8c9f65f 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -95,17 +95,13 @@
}
}
-bool HintSessionWrapper::useHintSession() {
- if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
- if (mHintSession) return true;
- // If session does not exist, create it;
- // this defers session creation until we try to actually use it.
- if (!mSessionValid) return false;
- return init();
-}
-
bool HintSessionWrapper::init() {
- if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+ // If it already exists, broke last time we tried this, shouldn't be running, or
+ // has bad argument values, don't even bother
+ if (mHintSession != nullptr || !mSessionValid || !Properties::useHintManager ||
+ !Properties::isDrawingEnabled() || mUiThreadId < 0 || mRenderThreadId < 0) {
+ return false;
+ }
// Assume that if we return before the end, it broke
mSessionValid = false;
@@ -130,7 +126,7 @@
}
void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
- if (!useHintSession()) return;
+ if (mHintSession == nullptr) return;
targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
if (targetWorkDurationNanos != mLastTargetWorkDuration &&
targetWorkDurationNanos > kSanityCheckLowerBound &&
@@ -142,7 +138,7 @@
}
void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
- if (!useHintSession()) return;
+ if (mHintSession == nullptr) return;
if (actualDurationNanos > kSanityCheckLowerBound &&
actualDurationNanos < kSanityCheckUpperBound) {
gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
@@ -150,7 +146,7 @@
}
void HintSessionWrapper::sendLoadResetHint() {
- if (!useHintSession()) return;
+ if (mHintSession == nullptr) return;
nsecs_t now = systemTime();
if (now - mLastFrameNotification > kResetHintTimeout) {
gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
@@ -159,7 +155,7 @@
}
void HintSessionWrapper::sendLoadIncreaseHint() {
- if (!useHintSession()) return;
+ if (mHintSession == nullptr) return;
gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP));
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index c0f7a57..f2f1298 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -34,10 +34,9 @@
void reportActualWorkDuration(long actualDurationNanos);
void sendLoadResetHint();
void sendLoadIncreaseHint();
+ bool init();
private:
- bool useHintSession();
- bool init();
APerformanceHintSession* mHintSession = nullptr;
nsecs_t mLastFrameNotification = 0;
diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
index 312b60b..bbdd98e 100644
--- a/libs/hwui/tests/common/BitmapAllocationTestUtils.h
+++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
@@ -56,10 +56,11 @@
template <class BaseScene>
static bool registerBitmapAllocationScene(std::string name, std::string description) {
- TestScene::registerScene({name + "GlTex", description + " (GlTex version).",
+ TestScene::registerScene({name + "Texture", description + " (Texture version).",
createBitmapAllocationScene<BaseScene, &allocateHeapBitmap>});
- TestScene::registerScene({name + "EglImage", description + " (EglImage version).",
+ TestScene::registerScene({name + "HardwareBuffer",
+ description + " (HardwareBuffer version).",
createBitmapAllocationScene<BaseScene, &allocateHardwareBitmap>});
return true;
}
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 3afb419..4485044 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -155,23 +155,12 @@
skcms_TransferFunction fn;
if (!colorSpace->isNumericalTransferFn(&fn)) {
- // pq with the default white point
- auto rec2020PQ = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
- if (SkColorSpace::Equals(colorSpace, rec2020PQ.get())) {
+ auto res = skcms_TransferFunction_getType(&fn);
+ if (res == skcms_TFType_PQish) {
return HAL_DATASPACE_BT2020_PQ;
}
- // standard PQ
- rec2020PQ = SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, SkNamedGamut::kRec2020);
- if (SkColorSpace::Equals(colorSpace, rec2020PQ.get())) {
- return HAL_DATASPACE_BT2020_PQ;
- }
- // HLG
- const auto hlgFn = GetHLGScaleTransferFunction();
- if (hlgFn.has_value()) {
- auto rec2020HLG = SkColorSpace::MakeRGB(hlgFn.value(), SkNamedGamut::kRec2020);
- if (SkColorSpace::Equals(colorSpace, rec2020HLG.get())) {
- return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
- }
+ if (res == skcms_TFType_HLGish) {
+ return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
}
LOG_ALWAYS_FATAL("Only select non-numerical transfer functions are supported");
}
diff --git a/libs/securebox/Android.bp b/libs/securebox/Android.bp
new file mode 100644
index 0000000..a29c03c
--- /dev/null
+++ b/libs/securebox/Android.bp
@@ -0,0 +1,8 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "securebox",
+ srcs: ["src/**/*.java"],
+}
diff --git a/libs/securebox/OWNERS b/libs/securebox/OWNERS
new file mode 100644
index 0000000..e160799
--- /dev/null
+++ b/libs/securebox/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java b/libs/securebox/src/com/android/security/SecureBox.java
similarity index 98%
rename from services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
rename to libs/securebox/src/com/android/security/SecureBox.java
index 51a37b3..0ebaff4 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
+++ b/libs/securebox/src/com/android/security/SecureBox.java
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.server.locksettings.recoverablekeystore;
+package com.android.security;
import android.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+
import java.math.BigInteger;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
@@ -41,6 +43,7 @@
import java.security.spec.EllipticCurve;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
+
import javax.crypto.AEADBadTagException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -380,7 +383,7 @@
* @param publicKey The public key.
* @return The key packed into a 65-byte array.
*/
- static byte[] encodePublicKey(PublicKey publicKey) {
+ public static byte[] encodePublicKey(PublicKey publicKey) {
ECPoint point = ((ECPublicKey) publicKey).getW();
byte[] x = point.getAffineX().toByteArray();
byte[] y = point.getAffineY().toByteArray();
@@ -394,8 +397,13 @@
return output;
}
- @VisibleForTesting
- static PublicKey decodePublicKey(byte[] keyBytes)
+ /**
+ * Decodes byte[] encoded public key.
+ *
+ * @param keyBytes encoded public key
+ * @return the public key
+ */
+ public static PublicKey decodePublicKey(byte[] keyBytes)
throws NoSuchAlgorithmException, InvalidKeyException {
BigInteger x =
new BigInteger(
diff --git a/libs/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp
new file mode 100644
index 0000000..7df546a
--- /dev/null
+++ b/libs/securebox/tests/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "SecureBoxTests",
+ srcs: [
+ "**/*.java",
+ ],
+ static_libs: [
+ "securebox",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "frameworks-base-testutils",
+ "junit",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "testables",
+ "testng",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+}
diff --git a/libs/securebox/tests/AndroidManifest.xml b/libs/securebox/tests/AndroidManifest.xml
new file mode 100644
index 0000000..3dc9563
--- /dev/null
+++ b/libs/securebox/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.security.tests">
+
+ <application android:debuggable="true" android:largeHeap="true">
+ <uses-library android:name="android.test.mock" />
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Tests for SecureBox"
+ android:targetPackage="com.android.security.tests">
+ </instrumentation>
+
+</manifest>
diff --git a/libs/securebox/tests/AndroidTest.xml b/libs/securebox/tests/AndroidTest.xml
new file mode 100644
index 0000000..54abd135
--- /dev/null
+++ b/libs/securebox/tests/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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 Tests for SecureBox">
+ <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="SecureBoxTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="SecureBoxTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.security.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java b/libs/securebox/tests/src/com/android/security/SecureBoxTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
rename to libs/securebox/tests/src/com/android/security/SecureBoxTest.java
index 34235bd..b6e2365 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
+++ b/libs/securebox/tests/src/com/android/security/SecureBoxTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.locksettings.recoverablekeystore;
+package com.android.security;
import static com.google.common.truth.Truth.assertThat;
@@ -24,11 +24,11 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.ArrayUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import com.android.internal.util.ArrayUtils;
-
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java
index bb97c78..90d9929 100644
--- a/location/java/android/location/Address.java
+++ b/location/java/android/location/Address.java
@@ -26,7 +26,7 @@
import android.os.Parcelable;
/**
- * A class representing an Address, i.e, a set of Strings describing a location.
+ * A class representing an Address, that is, a set of Strings describing a location.
*
* The address format is a simplified version of xAL (eXtensible Address Language)
* http://www.oasis-open.org/committees/ciq/ciq.html#6
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index f9f9fa4..11b5833 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -240,7 +240,11 @@
}
/**
- * Returns {@code true} if GNSS chipset supports on demand time, {@code false} otherwise.
+ * Returns {@code true} if GNSS chipset requests periodic time signal injection from the
+ * platform in addition to on-demand and occasional time updates, {@code false} otherwise.
+ *
+ * <p><em>Note: The naming of this capability and the behavior it controls differ substantially.
+ * This is the result of a historic implementation bug, b/73893222.</em>
*/
public boolean hasOnDemandTime() {
return (mTopFlags & TOP_HAL_CAPABILITY_ON_DEMAND_TIME) != 0;
diff --git a/media/aidl/android/media/soundtrigger_middleware/OWNERS b/media/aidl/android/media/soundtrigger_middleware/OWNERS
index e5d0370..01b2cb9 100644
--- a/media/aidl/android/media/soundtrigger_middleware/OWNERS
+++ b/media/aidl/android/media/soundtrigger_middleware/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
elaurent@google.com
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 813929e..0636451 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -20,6 +20,7 @@
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -407,7 +408,7 @@
public static final int STREAM_ACCESSIBILITY = AudioSystem.STREAM_ACCESSIBILITY;
/** @hide Used to identify the volume of audio streams for virtual assistant */
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public static final int STREAM_ASSISTANT = AudioSystem.STREAM_ASSISTANT;
/** Number of audio streams */
@@ -1038,7 +1039,7 @@
/** @hide */
@UnsupportedAppUsage
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setMasterMute(boolean mute, int flags) {
final IAudioService service = getService();
try {
@@ -1346,16 +1347,12 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags) {
Preconditions.checkNotNull(attr, "attr must not be null");
final IAudioService service = getService();
- try {
- service.setVolumeIndexForAttributes(attr, index, flags,
- getContext().getOpPackageName(), getContext().getAttributionTag());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ int groupId = getVolumeGroupIdForAttributes(attr);
+ setVolumeGroupVolumeIndex(groupId, index, flags);
}
/**
@@ -1370,15 +1367,12 @@
*/
@SystemApi
@IntRange(from = 0)
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
Preconditions.checkNotNull(attr, "attr must not be null");
final IAudioService service = getService();
- try {
- return service.getVolumeIndexForAttributes(attr);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ int groupId = getVolumeGroupIdForAttributes(attr);
+ return getVolumeGroupVolumeIndex(groupId);
}
/**
@@ -1391,15 +1385,12 @@
*/
@SystemApi
@IntRange(from = 0)
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
Preconditions.checkNotNull(attr, "attr must not be null");
final IAudioService service = getService();
- try {
- return service.getMaxVolumeIndexForAttributes(attr);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ int groupId = getVolumeGroupIdForAttributes(attr);
+ return getVolumeGroupMaxVolumeIndex(groupId);
}
/**
@@ -1412,12 +1403,186 @@
*/
@SystemApi
@IntRange(from = 0)
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
Preconditions.checkNotNull(attr, "attr must not be null");
final IAudioService service = getService();
+ int groupId = getVolumeGroupIdForAttributes(attr);
+ return getVolumeGroupMinVolumeIndex(groupId);
+ }
+
+ /**
+ * Returns the volume group id associated to the given {@link AudioAttributes}.
+ *
+ * @param attributes The {@link AudioAttributes} to consider.
+ * @return {@link android.media.audiopolicy.AudioVolumeGroup} id supporting the given
+ * {@link AudioAttributes} if found,
+ * {@code android.media.audiopolicy.AudioVolumeGroup.DEFAULT_VOLUME_GROUP} otherwise.
+ */
+ public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
+ Preconditions.checkNotNull(attributes, "Audio Attributes must not be null");
+ return AudioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes,
+ /* fallbackOnDefault= */ false);
+ }
+
+ /**
+ * Sets the volume index for a particular group associated to given id.
+ * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+ * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+ *
+ * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @param index The volume index to set. See
+ * {@link #getVolumeGroupMaxVolumeIndex(id)} for the largest valid value
+ * {@link #getVolumeGroupMinVolumeIndex(id)} for the lowest valid value.
+ * @param flags One or more flags.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ public void setVolumeGroupVolumeIndex(int groupId, int index, int flags) {
+ final IAudioService service = getService();
try {
- return service.getMinVolumeIndexForAttributes(attr);
+ service.setVolumeGroupVolumeIndex(groupId, index, flags,
+ getContext().getOpPackageName(), getContext().getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current volume index for a particular group associated to given id.
+ * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+ * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+ *
+ * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @return The current volume index for the stream.
+ * @hide
+ */
+ @SystemApi
+ @IntRange(from = 0)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ public int getVolumeGroupVolumeIndex(int groupId) {
+ final IAudioService service = getService();
+ try {
+ return service.getVolumeGroupVolumeIndex(groupId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the maximum volume index for a particular group associated to given id.
+ * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+ * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+ *
+ * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @return The maximum valid volume index for the {@link AudioAttributes}.
+ * @hide
+ */
+ @SystemApi
+ @IntRange(from = 0)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ public int getVolumeGroupMaxVolumeIndex(int groupId) {
+ final IAudioService service = getService();
+ try {
+ return service.getVolumeGroupMaxVolumeIndex(groupId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the minimum volume index for a particular group associated to given id.
+ * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+ * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+ *
+ * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @return The minimum valid volume index for the {@link AudioAttributes}.
+ * @hide
+ */
+ @SystemApi
+ @IntRange(from = 0)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ public int getVolumeGroupMinVolumeIndex(int groupId) {
+ final IAudioService service = getService();
+ try {
+ return service.getVolumeGroupMinVolumeIndex(groupId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adjusts the volume of a particular group associated to given id by one step in a direction.
+ * <p> If the volume group is associated to a stream type, it fallbacks on
+ * {@link #adjustStreamVolume(int, int, int)} for compatibility reason.
+ * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
+ * the volume group id supporting the given {@link AudioAttributes}.
+ *
+ * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @param direction The direction to adjust the volume. One of
+ * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
+ * {@link #ADJUST_SAME}.
+ * @param flags One or more flags.
+ * @throws SecurityException if the adjustment triggers a Do Not Disturb change and the caller
+ * is not granted notification policy access.
+ */
+ public void adjustVolumeGroupVolume(int groupId, int direction, int flags) {
+ IAudioService service = getService();
+ try {
+ service.adjustVolumeGroupVolume(groupId, direction, flags,
+ getContext().getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get last audible volume of the group associated to given id before it was muted.
+ * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
+ * the volume group id supporting the given {@link AudioAttributes}.
+ *
+ * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @return current volume if not muted, volume before muted otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+ @IntRange(from = 0)
+ public int getLastAudibleVolumeGroupVolume(int groupId) {
+ IAudioService service = getService();
+ try {
+ return service.getLastAudibleVolumeGroupVolume(groupId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current mute state for a particular volume group associated to the given id.
+ * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
+ * the volume group id supporting the given {@link AudioAttributes}.
+ *
+ * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @return The mute state for the given {@link android.media.audiopolicy.AudioVolumeGroup} id.
+ * @see #adjustVolumeGroupVolume(int, int, int)
+ */
+ public boolean isVolumeGroupMuted(int groupId) {
+ IAudioService service = getService();
+ try {
+ return service.isVolumeGroupMuted(groupId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1429,7 +1594,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setSupportedSystemUsages(@NonNull @AttributeSystemUsage int[] systemUsages) {
Objects.requireNonNull(systemUsages, "systemUsages must not be null");
final IAudioService service = getService();
@@ -1446,7 +1611,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public @NonNull @AttributeSystemUsage int[] getSupportedSystemUsages() {
final IAudioService service = getService();
try {
@@ -1555,7 +1720,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@UnsupportedAppUsage
public void forceVolumeControlStream(int streamType) {
final IAudioService service = getService();
@@ -1752,7 +1917,7 @@
* @return true if the operation was successful, false otherwise
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
@NonNull AudioDeviceAttributes device) {
return setPreferredDevicesForStrategy(strategy, Arrays.asList(device));
@@ -1768,7 +1933,7 @@
* device set for example)
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean removePreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy) {
Objects.requireNonNull(strategy);
try {
@@ -1791,7 +1956,7 @@
* ever set or if the strategy is invalid
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@Nullable
public AudioDeviceAttributes getPreferredDeviceForStrategy(
@NonNull AudioProductStrategy strategy) {
@@ -1813,7 +1978,7 @@
* @return true if the operation was successful, false otherwise
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy,
@NonNull List<AudioDeviceAttributes> devices) {
Objects.requireNonNull(strategy);
@@ -1843,7 +2008,7 @@
* @return list of the preferred devices for that strategy
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@NonNull
public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(
@NonNull AudioProductStrategy strategy) {
@@ -1867,7 +2032,7 @@
* @return true if the operation was successful, false otherwise
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setDeviceAsNonDefaultForStrategy(@NonNull AudioProductStrategy strategy,
@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(strategy);
@@ -1891,7 +2056,7 @@
* device set for example)
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean removeDeviceAsNonDefaultForStrategy(@NonNull AudioProductStrategy strategy,
@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(strategy);
@@ -1913,7 +2078,7 @@
* @return list of non-default devices for the strategy
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@NonNull
public List<AudioDeviceAttributes> getNonDefaultDevicesForStrategy(
@NonNull AudioProductStrategy strategy) {
@@ -1996,7 +2161,7 @@
* Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@Deprecated
public void addOnPreferredDeviceForStrategyChangedListener(
@NonNull @CallbackExecutor Executor executor,
@@ -2013,7 +2178,7 @@
* AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@Deprecated
public void removeOnPreferredDeviceForStrategyChangedListener(
@NonNull OnPreferredDeviceForStrategyChangedListener listener) {
@@ -2028,7 +2193,7 @@
* @throws SecurityException if the caller doesn't hold the required permission
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void addOnPreferredDevicesForStrategyChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnPreferredDevicesForStrategyChangedListener listener)
@@ -2046,7 +2211,7 @@
* @param listener
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void removeOnPreferredDevicesForStrategyChangedListener(
@NonNull OnPreferredDevicesForStrategyChangedListener listener) {
Objects.requireNonNull(listener);
@@ -2092,7 +2257,7 @@
* @throws SecurityException if the caller doesn't hold the required permission
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void addOnNonDefaultDevicesForStrategyChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnNonDefaultDevicesForStrategyChangedListener listener)
@@ -2112,7 +2277,7 @@
* @param listener
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void removeOnNonDefaultDevicesForStrategyChangedListener(
@NonNull OnNonDefaultDevicesForStrategyChangedListener listener) {
Objects.requireNonNull(listener);
@@ -2206,7 +2371,7 @@
* @return true if the operation was successful, false otherwise
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setPreferredDeviceForCapturePreset(@MediaRecorder.SystemSource int capturePreset,
@NonNull AudioDeviceAttributes device) {
return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
@@ -2220,7 +2385,7 @@
* device set for example)
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean clearPreferredDevicesForCapturePreset(
@MediaRecorder.SystemSource int capturePreset) {
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
@@ -2243,7 +2408,7 @@
*/
@NonNull
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(
@MediaRecorder.SystemSource int capturePreset) {
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
@@ -2315,7 +2480,7 @@
* @throws SecurityException if the caller doesn't hold the required permission
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void addOnPreferredDevicesForCapturePresetChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
@@ -2341,7 +2506,7 @@
* @param listener
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void removeOnPreferredDevicesForCapturePresetChangedListener(
@NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
Objects.requireNonNull(listener);
@@ -2785,7 +2950,7 @@
/**
* Start bluetooth SCO audio connection.
* <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * {@link Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* <p>This method can be used by applications wanting to send and received audio
* to/from a bluetooth SCO headset while the phone is not in call.
* <p>As the SCO connection establishment can take several seconds,
@@ -2842,7 +3007,7 @@
* @hide
* Start bluetooth SCO audio connection in virtual call mode.
* <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * {@link Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* <p>Similar to {@link #startBluetoothSco()} with explicit selection of virtual call mode.
* Telephony and communication applications (VoIP, Video Chat) should preferably select
* virtual call mode.
@@ -2866,7 +3031,7 @@
/**
* Stop bluetooth SCO audio connection.
* <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * {@link Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* <p>This method must be called by applications having requested the use of
* bluetooth SCO audio with {@link #startBluetoothSco()} when finished with the SCO
* connection or if connection fails.
@@ -3454,7 +3619,7 @@
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setHfpEnabled(boolean enable) {
AudioSystem.setParameters("hfp_enable=" + enable);
}
@@ -3463,7 +3628,7 @@
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setHfpVolume(int volume) {
AudioSystem.setParameters("hfp_volume=" + volume);
}
@@ -3472,7 +3637,7 @@
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setHfpSamplingRate(int rate) {
AudioSystem.setParameters("hfp_set_sampling_rate=" + rate);
}
@@ -3481,7 +3646,7 @@
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setBluetoothHeadsetProperties(@NonNull String name, boolean hasNrecEnabled,
boolean hasWbsEnabled) {
AudioSystem.setParameters("bt_headset_name=" + name
@@ -3493,7 +3658,7 @@
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setA2dpSuspended(boolean enable) {
AudioSystem.setParameters("A2dpSuspended=" + enable);
}
@@ -3506,7 +3671,7 @@
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setLeAudioSuspended(boolean enable) {
AudioSystem.setParameters("LeAudioSuspended=" + enable);
}
@@ -4316,7 +4481,7 @@
* @throws IllegalArgumentException
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
@@ -4357,8 +4522,8 @@
*/
@SystemApi
@RequiresPermission(anyOf= {
- android.Manifest.permission.MODIFY_PHONE_STATE,
- android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.MODIFY_AUDIO_ROUTING
})
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
@@ -4502,7 +4667,7 @@
* @throws IllegalArgumentException when trying to lock focus without an AudioPolicy
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
if (afr == null) {
throw new NullPointerException("Illegal null AudioFocusRequest");
@@ -4689,7 +4854,7 @@
* @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
@FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
if (afi == null) {
@@ -4728,7 +4893,7 @@
* @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
@NonNull AudioPolicy ap) {
if (afi == null) {
@@ -4988,11 +5153,11 @@
* @param policy the non-null {@link AudioPolicy} to register.
* @return {@link #ERROR} if there was an error communicating with the registration service
* or if the user doesn't have the required
- * {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} permission,
+ * {@link Manifest.permission#MODIFY_AUDIO_ROUTING} permission,
* {@link #SUCCESS} otherwise.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public int registerAudioPolicy(@NonNull AudioPolicy policy) {
return registerAudioPolicyStatic(policy);
}
@@ -5026,7 +5191,7 @@
* @param policy the non-null {@link AudioPolicy} to unregister.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) {
unregisterAudioPolicyAsyncStatic(policy);
}
@@ -5052,7 +5217,7 @@
* @param policy the non-null {@link AudioPolicy} to unregister.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void unregisterAudioPolicy(@NonNull AudioPolicy policy) {
Preconditions.checkNotNull(policy, "Illegal null AudioPolicy argument");
final IAudioService service = getService();
@@ -5853,8 +6018,8 @@
*/
@SystemApi
@RequiresPermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.QUERY_AUDIO_STATE
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
})
public @NonNull List<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
@@ -5929,8 +6094,8 @@
*/
@SystemApi
@RequiresPermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.QUERY_AUDIO_STATE
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
})
public void addOnDevicesForAttributesChangedListener(@NonNull AudioAttributes attributes,
@NonNull @CallbackExecutor Executor executor,
@@ -5960,8 +6125,8 @@
*/
@SystemApi
@RequiresPermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.QUERY_AUDIO_STATE
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
})
public void removeOnDevicesForAttributesChangedListener(
@NonNull OnDevicesForAttributesChangedListener listener) {
@@ -6119,7 +6284,10 @@
* @param deviceVolumeBehavior one of the device behaviors
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS
+ })
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@DeviceVolumeBehavior int deviceVolumeBehavior) {
// verify arguments (validity of device type is enforced in server)
@@ -6143,8 +6311,9 @@
*/
@SystemApi
@RequiresPermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.QUERY_AUDIO_STATE
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE,
+ Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS
})
public @DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
@@ -6165,8 +6334,9 @@
*/
@TestApi
@RequiresPermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.QUERY_AUDIO_STATE
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE,
+ Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS
})
public boolean isFullVolumeDevice() {
final AudioAttributes attributes = new AudioAttributes.Builder()
@@ -6189,7 +6359,7 @@
* {@hide}
*/
@UnsupportedAppUsage
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setWiredDeviceConnectionState(int device, int state, String address, String name) {
AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, address, name);
setWiredDeviceConnectionState(attributes, state);
@@ -6202,7 +6372,7 @@
* {@hide}
*/
@UnsupportedAppUsage
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
final IAudioService service = getService();
try {
@@ -6220,7 +6390,7 @@
* {@hide}
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
boolean connected) {
try {
@@ -6243,7 +6413,7 @@
* {@hide}
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice,
@Nullable BluetoothDevice previousDevice,
@NonNull BluetoothProfileConnectionInfo info) {
@@ -6363,7 +6533,7 @@
* or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setAdditionalOutputDeviceDelay(
@NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) {
Objects.requireNonNull(device);
@@ -6522,7 +6692,7 @@
* @return the RS2 value used for momentary exposure warnings
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
public float getRs2Value() {
try {
return getService().getRs2Value();
@@ -6536,7 +6706,7 @@
* Sets the RS2 value used for momentary exposure warnings
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
public void setRs2Value(float rs2Value) {
try {
getService().setRs2Value(rs2Value);
@@ -6550,7 +6720,7 @@
* @return the current computed sound dose value
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
public float getCsd() {
try {
return getService().getCsd();
@@ -6564,7 +6734,7 @@
* Sets the computed sound dose value to {@code csd}
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
public void setCsd(float csd) {
try {
getService().setCsd(csd);
@@ -6580,7 +6750,7 @@
* since this can affect the certification of a device with EN50332-3 regulation.
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
public void forceUseFrameworkMel(boolean useFrameworkMel) {
try {
getService().forceUseFrameworkMel(useFrameworkMel);
@@ -6594,7 +6764,7 @@
* Forces the computation of CSD on all output devices.
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
try {
getService().forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
@@ -6608,7 +6778,7 @@
* Returns whether CSD is enabled on this device.
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
public boolean isCsdEnabled() {
try {
return getService().isCsdEnabled();
@@ -7642,7 +7812,7 @@
*
* @return true if successful, otherwise false
*/
- @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
public boolean setEncodedSurroundMode(@EncodedSurroundOutputMode int mode) {
try {
return getService().setEncodedSurroundMode(mode);
@@ -7694,7 +7864,7 @@
* @param enabled the required surround format state, true for enabled, false for disabled
* @return true if successful, otherwise false
*/
- @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
public boolean setSurroundFormatEnabled(
@AudioFormat.SurroundSoundEncoding int audioFormat, boolean enabled) {
try {
@@ -7758,7 +7928,7 @@
* Ultrasound playback and capture, false otherwise.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+ @RequiresPermission(Manifest.permission.ACCESS_ULTRASOUND)
public boolean isUltrasoundSupported() {
try {
return getService().isUltrasoundSupported();
@@ -7779,7 +7949,7 @@
* open. False otherwise.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
+ @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD)
public boolean isHotwordStreamSupported(boolean lookbackAudio) {
try {
return getService().isHotwordStreamSupported(lookbackAudio);
@@ -7803,7 +7973,7 @@
*/
@SystemApi
@NonNull
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public static List<AudioProductStrategy> getAudioProductStrategies() {
final IAudioService service = getService();
try {
@@ -7823,7 +7993,7 @@
*/
@SystemApi
@NonNull
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public static List<AudioVolumeGroup> getAudioVolumeGroups() {
final IAudioService service = getService();
try {
@@ -8075,7 +8245,7 @@
/** @hide
* TODO: make this a @SystemApi */
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setMultiAudioFocusEnabled(boolean enabled) {
try {
getService().setMultiAudioFocusEnabled(enabled);
@@ -8119,7 +8289,7 @@
* latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
* {@link #MODE_IN_CALL}. Note that <code>MODE_IN_CALL</code> can only be selected by the main
* telephony application with permission
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
+ * {@link Manifest.permission#MODIFY_PHONE_STATE}.
* <p> If the requested devices is not currently available, the request will be rejected and
* the method will return false.
* <p>This API replaces the following deprecated APIs:
@@ -8159,7 +8329,8 @@
Objects.requireNonNull(device);
try {
if (device.getId() == 0) {
- throw new IllegalArgumentException("In valid device: " + device);
+ Log.w(TAG, "setCommunicationDevice: device not found: " + device);
+ return false;
}
return getService().setCommunicationDevice(mICallBack, device.getId());
} catch (RemoteException e) {
@@ -8386,7 +8557,7 @@
*/
@TestApi
@SystemApi
- @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
+ @RequiresPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION)
public boolean isPstnCallAudioInterceptable() {
final IAudioService service = getService();
try {
@@ -8487,7 +8658,7 @@
*/
@TestApi
@SystemApi
- @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
+ @RequiresPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION)
public @NonNull AudioTrack getCallUplinkInjectionAudioTrack(@NonNull AudioFormat format) {
Objects.requireNonNull(format);
checkCallRedirectionFormat(format, true /* isOutput */);
@@ -8559,7 +8730,7 @@
*/
@TestApi
@SystemApi
- @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
+ @RequiresPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION)
public @NonNull AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull AudioFormat format) {
Objects.requireNonNull(format);
checkCallRedirectionFormat(format, false /* isOutput */);
@@ -8668,7 +8839,7 @@
* @see #registerMuteAwaitConnectionCallback(Executor, AudioManager.MuteAwaitConnectionCallback)
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void muteAwaitConnection(@NonNull int[] usagesToMute,
@NonNull AudioDeviceAttributes device,
long timeout, @NonNull TimeUnit timeUnit) throws IllegalStateException {
@@ -8698,7 +8869,7 @@
* or because it timed out)
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
try {
return getService().getMutingExpectedDevice();
@@ -8718,7 +8889,7 @@
* {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void cancelMuteAwaitConnection(@NonNull AudioDeviceAttributes device)
throws IllegalStateException {
Objects.requireNonNull(device);
@@ -8797,7 +8968,7 @@
* @param callback the callback to register
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void registerMuteAwaitConnectionCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull MuteAwaitConnectionCallback callback) {
@@ -8821,7 +8992,7 @@
* @param callback the callback to unregister
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void unregisterMuteAwaitConnectionCallback(
@NonNull MuteAwaitConnectionCallback callback) {
synchronized (mMuteAwaitConnectionListenerLock) {
@@ -8843,7 +9014,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void addAssistantServicesUids(@NonNull int[] assistantUids) {
try {
getService().addAssistantServicesUids(assistantUids);
@@ -8860,7 +9031,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void removeAssistantServicesUids(@NonNull int[] assistantUids) {
try {
getService().removeAssistantServicesUids(assistantUids);
@@ -8885,7 +9056,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public @NonNull int[] getAssistantServicesUids() {
try {
int[] uids = getService().getAssistantServicesUids();
@@ -8910,7 +9081,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setActiveAssistantServiceUids(@NonNull int[] assistantUids) {
try {
getService().setActiveAssistantServiceUids(assistantUids);
@@ -8928,7 +9099,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public @NonNull int[] getActiveAssistantServicesUids() {
try {
int[] uids = getService().getActiveAssistantServiceUids();
@@ -9002,7 +9173,7 @@
* @see #getPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
* @see #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
*/
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
public boolean setPreferredMixerAttributes(@NonNull AudioAttributes attributes,
@NonNull AudioDeviceInfo device,
@NonNull AudioMixerAttributes mixerAttributes) {
@@ -9056,7 +9227,7 @@
* @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
* @see #getPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
*/
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
public boolean clearPreferredMixerAttributes(
@NonNull AudioAttributes attributes,
@NonNull AudioDeviceInfo device) {
@@ -9176,7 +9347,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean supportsBluetoothVariableLatency() {
try {
return getService().supportsBluetoothVariableLatency();
@@ -9193,7 +9364,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setBluetoothVariableLatencyEnabled(boolean enabled) {
try {
getService().setBluetoothVariableLatencyEnabled(enabled);
@@ -9207,7 +9378,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean isBluetoothVariableLatencyEnabled() {
try {
return getService().isBluetoothVariableLatencyEnabled();
diff --git a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl b/media/java/android/media/AudioPresentation.aidl
similarity index 64%
copy from core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
copy to media/java/android/media/AudioPresentation.aidl
index b30a12a..3fb5a5b 100644
--- a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
+++ b/media/java/android/media/AudioPresentation.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-package android.credentials;
+package android.media;
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IUnregisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+parcelable AudioPresentation;
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 05f3c5a..a413545 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -49,7 +52,7 @@
* Applications that parse media streams and extract presentation information on their own
* can create instances of AudioPresentation by using {@link AudioPresentation.Builder} class.
*/
-public final class AudioPresentation {
+public final class AudioPresentation implements Parcelable {
private final int mPresentationId;
private final int mProgramId;
private final ULocale mLanguage;
@@ -126,7 +129,7 @@
private final boolean mAudioDescriptionAvailable;
private final boolean mSpokenSubtitlesAvailable;
private final boolean mDialogueEnhancementAvailable;
- private final Map<ULocale, CharSequence> mLabels;
+ private final HashMap<ULocale, String> mLabels;
/**
* No preferred reproduction channel layout.
@@ -160,9 +163,14 @@
public static final int MASTERED_FOR_HEADPHONE = 4;
/**
- * This ID is reserved. No items can be explicitly assigned this ID.
+ * Unknown audio presentation ID, this indicates audio presentation ID is not selected.
*/
- private static final int UNKNOWN_ID = -1;
+ public static final int PRESENTATION_ID_UNKNOWN = -1;
+
+ /**
+ * Unknown audio program ID, this indicates audio program ID is not selected.
+ */
+ public static final int PROGRAM_ID_UNKNOWN = -1;
/**
* This allows an application developer to construct an AudioPresentation object with all the
@@ -191,7 +199,7 @@
boolean audioDescriptionAvailable,
boolean spokenSubtitlesAvailable,
boolean dialogueEnhancementAvailable,
- @NonNull Map<ULocale, CharSequence> labels) {
+ @NonNull Map<ULocale, String> labels) {
mPresentationId = presentationId;
mProgramId = programId;
mLanguage = language;
@@ -199,7 +207,18 @@
mAudioDescriptionAvailable = audioDescriptionAvailable;
mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
- mLabels = new HashMap<ULocale, CharSequence>(labels);
+ mLabels = new HashMap<ULocale, String>(labels);
+ }
+
+ private AudioPresentation(@NonNull Parcel in) {
+ mPresentationId = in.readInt();
+ mProgramId = in.readInt();
+ mLanguage = in.readSerializable(ULocale.class.getClassLoader(), ULocale.class);
+ mMasteringIndication = in.readInt();
+ mAudioDescriptionAvailable = in.readBoolean();
+ mSpokenSubtitlesAvailable = in.readBoolean();
+ mDialogueEnhancementAvailable = in.readBoolean();
+ mLabels = in.readSerializable(HashMap.class.getClassLoader(), HashMap.class);
}
/**
@@ -225,13 +244,13 @@
*/
public Map<Locale, String> getLabels() {
Map<Locale, String> localeLabels = new HashMap<Locale, String>(mLabels.size());
- for (Map.Entry<ULocale, CharSequence> entry : mLabels.entrySet()) {
- localeLabels.put(entry.getKey().toLocale(), entry.getValue().toString());
+ for (Map.Entry<ULocale, String> entry : mLabels.entrySet()) {
+ localeLabels.put(entry.getKey().toLocale(), entry.getValue());
}
return localeLabels;
}
- private Map<ULocale, CharSequence> getULabels() {
+ private Map<ULocale, String> getULabels() {
return mLabels;
}
@@ -335,13 +354,13 @@
*/
public static final class Builder {
private final int mPresentationId;
- private int mProgramId = UNKNOWN_ID;
+ private int mProgramId = PROGRAM_ID_UNKNOWN;
private ULocale mLanguage = new ULocale("");
private int mMasteringIndication = MASTERING_NOT_INDICATED;
private boolean mAudioDescriptionAvailable = false;
private boolean mSpokenSubtitlesAvailable = false;
private boolean mDialogueEnhancementAvailable = false;
- private Map<ULocale, CharSequence> mLabels = new HashMap<ULocale, CharSequence>();
+ private HashMap<ULocale, String> mLabels = new HashMap<ULocale, String>();
/**
* Create a {@link Builder}. Any field that should be included in the
@@ -402,7 +421,10 @@
* @param labels Text label indexed by its locale corresponding to the language code.
*/
public @NonNull Builder setLabels(@NonNull Map<ULocale, CharSequence> labels) {
- mLabels = new HashMap<ULocale, CharSequence>(labels);
+ mLabels.clear();
+ for (Map.Entry<ULocale, CharSequence> entry : labels.entrySet()) {
+ mLabels.put(entry.getKey(), entry.getValue().toString());
+ }
return this;
}
@@ -448,4 +470,35 @@
mDialogueEnhancementAvailable, mLabels);
}
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(getPresentationId());
+ dest.writeInt(getProgramId());
+ dest.writeSerializable(getULocale());
+ dest.writeInt(getMasteringIndication());
+ dest.writeBoolean(hasAudioDescription());
+ dest.writeBoolean(hasSpokenSubtitles());
+ dest.writeBoolean(hasDialogueEnhancement());
+ dest.writeSerializable(mLabels);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AudioPresentation> CREATOR =
+ new Parcelable.Creator<AudioPresentation>() {
+ @Override
+ public AudioPresentation createFromParcel(@NonNull Parcel in) {
+ return new AudioPresentation(in);
+ }
+
+ @Override
+ public AudioPresentation[] newArray(int size) {
+ return new AudioPresentation[size];
+ }
+ };
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 50749e7..948fef4 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -28,6 +28,8 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
+import android.content.AttributionSource.ScopedParcelState;
import android.content.Context;
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioMixingRule;
@@ -39,6 +41,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.Log;
@@ -822,15 +825,20 @@
int[] session = new int[1];
session[0] = resolvePlaybackSessionId(context, sessionId);
+ AttributionSource attributionSource = context == null
+ ? AttributionSource.myAttributionSource() : context.getAttributionSource();
+
// native initialization
- int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
- sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
- mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
- offload, encapsulationMode, tunerConfiguration,
- getCurrentOpPackageName());
- if (initResult != SUCCESS) {
- loge("Error code "+initResult+" when initializing AudioTrack.");
- return; // with mState == STATE_UNINITIALIZED
+ try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
+ int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
+ sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
+ mNativeBufferSizeInBytes, mDataLoadMode, session,
+ attributionSourceState.getParcel(), 0 /*nativeTrackInJavaObj*/, offload,
+ encapsulationMode, tunerConfiguration, getCurrentOpPackageName());
+ if (initResult != SUCCESS) {
+ loge("Error code " + initResult + " when initializing AudioTrack.");
+ return; // with mState == STATE_UNINITIALIZED
+ }
}
mSampleRate = sampleRate[0];
@@ -902,23 +910,27 @@
// *Native* AudioTrack, so the attributes parameters to native_setup() are ignored.
int[] session = { 0 };
int[] rates = { 0 };
- int initResult = native_setup(new WeakReference<AudioTrack>(this),
- null /*mAttributes - NA*/,
- rates /*sampleRate - NA*/,
- 0 /*mChannelMask - NA*/,
- 0 /*mChannelIndexMask - NA*/,
- 0 /*mAudioFormat - NA*/,
- 0 /*mNativeBufferSizeInBytes - NA*/,
- 0 /*mDataLoadMode - NA*/,
- session,
- nativeTrackInJavaObj,
- false /*offload*/,
- ENCAPSULATION_MODE_NONE,
- null /* tunerConfiguration */,
- "" /* opPackagename */);
- if (initResult != SUCCESS) {
- loge("Error code "+initResult+" when initializing AudioTrack.");
- return; // with mState == STATE_UNINITIALIZED
+ try (ScopedParcelState attributionSourceState =
+ AttributionSource.myAttributionSource().asScopedParcelState()) {
+ int initResult = native_setup(new WeakReference<AudioTrack>(this),
+ null /*mAttributes - NA*/,
+ rates /*sampleRate - NA*/,
+ 0 /*mChannelMask - NA*/,
+ 0 /*mChannelIndexMask - NA*/,
+ 0 /*mAudioFormat - NA*/,
+ 0 /*mNativeBufferSizeInBytes - NA*/,
+ 0 /*mDataLoadMode - NA*/,
+ session,
+ attributionSourceState.getParcel(),
+ nativeTrackInJavaObj,
+ false /*offload*/,
+ ENCAPSULATION_MODE_NONE,
+ null /* tunerConfiguration */,
+ "" /* opPackagename */);
+ if (initResult != SUCCESS) {
+ loge("Error code " + initResult + " when initializing AudioTrack.");
+ return; // with mState == STATE_UNINITIALIZED
+ }
}
mSessionId = session[0];
@@ -4371,9 +4383,9 @@
private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
Object /*AudioAttributes*/ attributes,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
- int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
- boolean offload, int encapsulationMode, Object tunerConfiguration,
- @NonNull String opPackageName);
+ int buffSizeInBytes, int mode, int[] sessionId, @NonNull Parcel attributionSource,
+ long nativeAudioTrack, boolean offload, int encapsulationMode,
+ Object tunerConfiguration, @NonNull String opPackageName);
private native final void native_finalize();
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5ba7891..3e0356f 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -138,18 +138,25 @@
@EnforcePermission("MODIFY_AUDIO_ROUTING")
List<AudioVolumeGroup> getAudioVolumeGroups();
- @EnforcePermission("MODIFY_AUDIO_ROUTING")
- void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags,
- String callingPackage, in String attributionTag);
+ @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+ void setVolumeGroupVolumeIndex(int groupId, int index, int flags, String callingPackage,
+ in String attributionTag);
- @EnforcePermission("MODIFY_AUDIO_ROUTING")
- int getVolumeIndexForAttributes(in AudioAttributes aa);
+ @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+ int getVolumeGroupVolumeIndex(int groupId);
- @EnforcePermission("MODIFY_AUDIO_ROUTING")
- int getMaxVolumeIndexForAttributes(in AudioAttributes aa);
+ @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+ int getVolumeGroupMaxVolumeIndex(int groupId);
- @EnforcePermission("MODIFY_AUDIO_ROUTING")
- int getMinVolumeIndexForAttributes(in AudioAttributes aa);
+ @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+ int getVolumeGroupMinVolumeIndex(int groupId);
+
+ @EnforcePermission("QUERY_AUDIO_STATE")
+ int getLastAudibleVolumeGroupVolume(int groupId);
+
+ boolean isVolumeGroupMuted(int groupId);
+
+ void adjustVolumeGroupVolume(int groupId, int direction, int flags, String callingPackage);
@EnforcePermission("QUERY_AUDIO_STATE")
int getLastAudibleStreamVolume(int streamType);
@@ -405,10 +412,11 @@
oneway void setRttEnabled(in boolean rttEnabled);
- @EnforcePermission("MODIFY_AUDIO_ROUTING")
+ @EnforcePermission(anyOf = {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SYSTEM_SETTINGS"})
void setDeviceVolumeBehavior(in AudioDeviceAttributes device,
in int deviceVolumeBehavior, in String pkgName);
+ @EnforcePermission(anyOf = {"MODIFY_AUDIO_ROUTING", "QUERY_AUDIO_STATE", "MODIFY_AUDIO_SYSTEM_SETTINGS"})
int getDeviceVolumeBehavior(in AudioDeviceAttributes device);
// WARNING: read warning at top of file, new methods that need to be used by native
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index aa7e4df..f739365 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -94,4 +94,5 @@
void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, int volume);
void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
+ void showMediaOutputSwitcher(String packageName);
}
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 44bb56e..7ac8446 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -85,6 +85,7 @@
public static int getNumPlanesForHardwareBufferFormat(int hardwareBufferFormat) {
switch(hardwareBufferFormat) {
case HardwareBuffer.YCBCR_420_888:
+ case HardwareBuffer.YCBCR_P010:
return 3;
case HardwareBuffer.RGBA_8888:
case HardwareBuffer.RGBX_8888:
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index a7959c5..24ec227 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -461,6 +461,19 @@
}
/**
+ * Shows the system UI output switcher.
+ */
+ public void showSystemOutputSwitcher() {
+ synchronized (mLock) {
+ try {
+ mMediaRouterService.showMediaOutputSwitcher(mPackageName);
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Sets the {@link RouteListingPreference} of the app associated to this media router.
*
* <p>Use this method to inform the system UI of the routes that you would like to list for
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index e6d95eb6..9b238e1 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -236,7 +236,7 @@
* which enables remote providers to efficiently cache and invalidate
* thumbnails.
*
- * @param file The audio file.
+ * @param file The image file.
* @param size The desired thumbnail size.
* @throws IOException If any trouble was encountered while generating or
* loading the thumbnail, or if
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 0198419..0289aa3 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -233,6 +233,16 @@
/**
* @hide
+ * @return the product strategy name (which is the generalisation of Car Audio Usage / legacy
+ * routing_strategy linked to {@link AudioAttributes#getUsage()}).
+ */
+ @SystemApi
+ @NonNull public String getName() {
+ return mName;
+ }
+
+ /**
+ * @hide
* @return first {@link AudioAttributes} associated to this product strategy.
*/
@SystemApi
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 99d1f8d..c259f9a 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -33,7 +33,7 @@
IMediaProjection createProjection(int uid, String packageName, int type,
boolean permanentGrant);
- boolean isValidMediaProjection(IMediaProjection projection);
+ boolean isCurrentProjection(IMediaProjection projection);
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS
index e5d0370..01b2cb9 100644
--- a/media/java/android/media/soundtrigger/OWNERS
+++ b/media/java/android/media/soundtrigger/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
elaurent@google.com
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index d55d287..c4f60c3 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.content.ComponentName;
+import android.media.AudioPresentation;
import android.media.tv.AdBuffer;
import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
@@ -37,6 +38,8 @@
void onSessionReleased(int seq);
void onSessionEvent(in String name, in Bundle args, int seq);
void onChannelRetuned(in Uri channelUri, int seq);
+ void onAudioPresentationsChanged(in List<AudioPresentation> AudioPresentations, int seq);
+ void onAudioPresentationSelected(int presentationId, int programId, int seq);
void onTracksChanged(in List<TvTrackInfo> tracks, int seq);
void onTrackSelected(int type, in String trackId, int seq);
void onVideoAvailable(int seq);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index f7c1e3c..746cfa2 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -16,9 +16,11 @@
package android.media.tv;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Rect;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.AdBuffer;
import android.media.tv.AdRequest;
@@ -63,7 +65,7 @@
void addBlockedRating(in String rating, int userId);
void removeBlockedRating(in String rating, int userId);
- void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession,
+ void createSession(in ITvInputClient client, in String inputId, in AttributionSource tvAppAttributionSource, boolean isRecordingSession,
int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
int getClientPid(in String sessionId);
@@ -77,6 +79,8 @@
void tune(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
+ void selectAudioPresentation(in IBinder sessionToken, int presentationId, int programId,
+ int userId);
void setInteractiveAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index 64a23a2..be73c0c 100755
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.content.AttributionSource;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSessionCallback;
@@ -30,7 +31,7 @@
oneway void registerCallback(in ITvInputServiceCallback callback);
oneway void unregisterCallback(in ITvInputServiceCallback callback);
oneway void createSession(in InputChannel channel, in ITvInputSessionCallback callback,
- in String inputId, in String sessionId);
+ in String inputId, in String sessionId, in AttributionSource tvAppAttributionSource);
oneway void createRecordingSession(in ITvInputSessionCallback callback, in String inputId,
in String sessionId);
List<String> getAvailableExtensionInterfaceNames();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 326b98d..e6c09a9 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.graphics.Rect;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.AdBuffer;
import android.media.tv.AdRequest;
@@ -41,6 +42,7 @@
void setVolume(float volume);
void tune(in Uri channelUri, in Bundle params);
void setCaptionEnabled(boolean enabled);
+ void selectAudioPresentation(int presentationId, int programId);
void selectTrack(int type, in String trackId);
void setInteractiveAppNotificationEnabled(boolean enable);
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 8216622..0111f09 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.media.AudioPresentation;
import android.media.tv.AdBuffer;
import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
@@ -34,6 +35,8 @@
void onSessionCreated(ITvInputSession session, in IBinder hardwareSessionToken);
void onSessionEvent(in String name, in Bundle args);
void onChannelRetuned(in Uri channelUri);
+ void onAudioPresentationsChanged(in List<AudioPresentation> tvAudioPresentations);
+ void onAudioPresentationSelected(int presentationId, int programId);
void onTracksChanged(in List<TvTrackInfo> tracks);
void onTrackSelected(int type, in String trackId);
void onVideoAvailable();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 634f102..847762f 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -30,7 +30,6 @@
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.Surface;
-
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
@@ -75,6 +74,7 @@
private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 26;
private static final int DO_REQUEST_AD = 27;
private static final int DO_NOTIFY_AD_BUFFER = 28;
+ private static final int DO_SELECT_AUDIO_PRESENTATION = 29;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -240,6 +240,12 @@
mTvInputRecordingSessionImpl.resumeRecording((Bundle) msg.obj);
break;
}
+ case DO_SELECT_AUDIO_PRESENTATION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputSessionImpl.selectAudioPresentation(args.argi1, args.argi2);
+ args.recycle();
+ break;
+ }
case DO_REQUEST_BROADCAST_INFO: {
mTvInputSessionImpl.requestBroadcastInfo((BroadcastInfoRequest) msg.obj);
break;
@@ -275,8 +281,8 @@
+ "Consider handling the tune request in a separate thread.");
}
if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
- throw new RuntimeException("Too much time to handle a request. (type=" + msg.what +
- ", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
+ throw new RuntimeException("Too much time to handle a request. (type=" + msg.what
+ + ", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
}
}
}
@@ -323,6 +329,12 @@
}
@Override
+ public void selectAudioPresentation(int presentationId, int programId) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_SELECT_AUDIO_PRESENTATION,
+ presentationId, programId));
+ }
+
+ @Override
public void selectTrack(int type, String trackId) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_SELECT_TRACK, type, trackId));
}
@@ -436,7 +448,7 @@
}
private final class TvInputEventReceiver extends InputEventReceiver {
- public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index e8127df..a4e2212 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -26,11 +26,13 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat.Encoding;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.interactive.TvInteractiveAppManager;
import android.net.Uri;
@@ -54,9 +56,7 @@
import android.view.KeyEvent;
import android.view.Surface;
import android.view.View;
-
import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -550,6 +550,27 @@
}
/**
+ * This is called when the audio presentation information of the session has been changed.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param audioPresentations An updated list of selectable audio presentations.
+ */
+ public void onAudioPresentationsChanged(Session session,
+ List<AudioPresentation> audioPresentations) {
+ }
+
+ /**
+ * This is called when an audio presentation is selected.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param presentationId The ID of the selected audio presentation.
+ * @param programId The ID of the program providing the selected audio presentation.
+ */
+ public void onAudioPresentationSelected(Session session, int presentationId,
+ int programId) {
+ }
+
+ /**
* This is called when the track information of the session has been changed.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
@@ -777,6 +798,25 @@
});
}
+ void postAudioPresentationsChanged(final List<AudioPresentation> audioPresentations) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onAudioPresentationsChanged(mSession, audioPresentations);
+ }
+ });
+ }
+
+ void postAudioPresentationSelected(final int presentationId, final int programId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onAudioPresentationSelected(mSession, presentationId,
+ programId);
+ }
+ });
+ }
+
void postTracksChanged(final List<TvTrackInfo> tracks) {
mHandler.post(new Runnable() {
@Override
@@ -1234,6 +1274,36 @@
record.postChannelRetuned(channelUri);
}
}
+ @Override
+ public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations,
+ int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ if (record.mSession.updateAudioPresentations(audioPresentations)) {
+ record.postAudioPresentationsChanged(audioPresentations);
+ }
+ }
+ }
+
+ @Override
+ public void onAudioPresentationSelected(int presentationId, int programId, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ if (record.mSession.updateAudioPresentationSelection(presentationId,
+ programId)) {
+ record.postAudioPresentationSelected(presentationId, programId);
+ }
+ }
+ }
+
@Override
public void onTracksChanged(List<TvTrackInfo> tracks, int seq) {
@@ -1910,13 +1980,15 @@
* of the given TV input.
*
* @param inputId The ID of the TV input.
+ * @param tvAppAttributionSource The Attribution Source of the TV App.
* @param callback A callback used to receive the created session.
* @param handler A {@link Handler} that the session creation will be delivered to.
* @hide
*/
- public void createSession(@NonNull String inputId, @NonNull final SessionCallback callback,
- @NonNull Handler handler) {
- createSessionInternal(inputId, false, callback, handler);
+ public void createSession(@NonNull String inputId,
+ @NonNull AttributionSource tvAppAttributionSource,
+ @NonNull final SessionCallback callback, @NonNull Handler handler) {
+ createSessionInternal(inputId, tvAppAttributionSource, false, callback, handler);
}
/**
@@ -1941,7 +2013,7 @@
* @param useCase the use case type of the client.
* {@see TvInputService#PriorityHintUseCaseType}.
* @param sessionId the unique id of the session owned by the client.
- * {@see TvInputService#onCreateSession(String, String)}.
+ * {@see TvInputService#onCreateSession(String, String, AttributionSource)}.
*
* @return the use case priority value for the given use case type and the client's foreground
* or background status.
@@ -1992,11 +2064,11 @@
*/
public void createRecordingSession(@NonNull String inputId,
@NonNull final SessionCallback callback, @NonNull Handler handler) {
- createSessionInternal(inputId, true, callback, handler);
+ createSessionInternal(inputId, null, true, callback, handler);
}
- private void createSessionInternal(String inputId, boolean isRecordingSession,
- SessionCallback callback, Handler handler) {
+ private void createSessionInternal(String inputId, AttributionSource tvAppAttributionSource,
+ boolean isRecordingSession, SessionCallback callback, Handler handler) {
Preconditions.checkNotNull(inputId);
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(handler);
@@ -2005,7 +2077,8 @@
int seq = mNextSeq++;
mSessionCallbackRecordMap.put(seq, record);
try {
- mService.createSession(mClient, inputId, isRecordingSession, seq, mUserId);
+ mService.createSession(
+ mClient, inputId, tvAppAttributionSource, isRecordingSession, seq, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2176,8 +2249,8 @@
* @param deviceId The device ID to acquire Hardware for.
* @param info The TV input which will use the acquired Hardware.
* @param tvInputSessionId a String returned to TIS when the session was created.
- * {@see TvInputService#onCreateSession(String, String)}. If null, the client will be
- * treated as a background app.
+ * {@see TvInputService#onCreateSession(String, String, AttributionSource)}. If null, the
+ * client will be treated as a background app.
* @param priorityHint The use case of the client. {@see TvInputService#PriorityHintUseCaseType}
* @param executor the executor on which the listener would be invoked.
* @param callback A callback to receive updates on Hardware.
@@ -2399,12 +2472,18 @@
private final Object mMetadataLock = new Object();
// @GuardedBy("mMetadataLock")
+ private final List<AudioPresentation> mAudioPresentations = new ArrayList<>();
+ // @GuardedBy("mMetadataLock")
private final List<TvTrackInfo> mAudioTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
private final List<TvTrackInfo> mVideoTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
+ private int mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
+ // @GuardedBy("mMetadataLock")
+ private int mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
+ // @GuardedBy("mMetadataLock")
private String mSelectedAudioTrackId;
// @GuardedBy("mMetadataLock")
private String mSelectedVideoTrackId;
@@ -2552,9 +2631,12 @@
return;
}
synchronized (mMetadataLock) {
+ mAudioPresentations.clear();
mAudioTracks.clear();
mVideoTracks.clear();
mSubtitleTracks.clear();
+ mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
+ mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
mSelectedAudioTrackId = null;
mSelectedVideoTrackId = null;
mSelectedSubtitleTrackId = null;
@@ -2586,6 +2668,119 @@
}
/**
+ * Selects an audio presentation
+ *
+ * @param presentationId The ID of the audio presentation to select.
+ * @param programId The ID of the program offering the selected audio presentation.
+ * @see #getAudioPresentations
+ */
+ public void selectAudioPresentation(int presentationId, int programId) {
+ synchronized (mMetadataLock) {
+ if (presentationId != AudioPresentation.PRESENTATION_ID_UNKNOWN
+ && !containsAudioPresentation(mAudioPresentations, presentationId)) {
+ Log.w(TAG, "Invalid audio presentation id: " + presentationId);
+ return;
+ }
+ }
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.selectAudioPresentation(mToken, presentationId, programId, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private boolean containsAudioPresentation(List<AudioPresentation> audioPresentations,
+ int presentationId) {
+ synchronized (mMetadataLock) {
+ for (AudioPresentation audioPresentation : audioPresentations) {
+ if (audioPresentation.getPresentationId() == presentationId) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns a list of audio presentations.
+ *
+ * @return the list of audio presentations.
+ * Returns empty AudioPresentation list if no presentations are available.
+ */
+ public List<AudioPresentation> getAudioPresentations() {
+ synchronized (mMetadataLock) {
+ if (mAudioPresentations == null) {
+ return new ArrayList<AudioPresentation>();
+ }
+ return new ArrayList<AudioPresentation>(mAudioPresentations);
+ }
+ }
+
+ /**
+ * Returns the program ID of the selected audio presentation.
+ *
+ * @return The ID of the program providing the selected audio presentation.
+ * Returns {@value AudioPresentation.PROGRAM_ID_UNKNOWN} if no audio presentation has
+ * been selected from a program.
+ * @see #selectAudioPresentation
+ */
+ public int getSelectedProgramId() {
+ synchronized (mMetadataLock) {
+ return mSelectedAudioProgramId;
+ }
+ }
+
+ /**
+ * Returns the presentation ID of the selected audio presentation.
+ *
+ * @return The ID of the selected audio presentation.
+ * Returns {@value AudioPresentation.PRESENTATION_ID_UNKNOWN} if no audio presentation
+ * has been selected.
+ * @see #selectAudioPresentation
+ */
+ public int getSelectedAudioPresentationId() {
+ synchronized (mMetadataLock) {
+ return mSelectedAudioPresentationId;
+ }
+ }
+
+ /**
+ * Responds to onAudioPresentationsChanged() and updates the internal audio presentation
+ * information.
+ * @return true if there is an update.
+ */
+ boolean updateAudioPresentations(List<AudioPresentation> audioPresentations) {
+ synchronized (mMetadataLock) {
+ mAudioPresentations.clear();
+ for (AudioPresentation presentation : audioPresentations) {
+ mAudioPresentations.add(presentation);
+ }
+ return !mAudioPresentations.isEmpty();
+ }
+ }
+
+ /**
+ * Responds to onAudioPresentationSelected() and updates the internal audio presentation
+ * selection information.
+ * @return true if there is an update.
+ */
+ boolean updateAudioPresentationSelection(int presentationId, int programId) {
+ synchronized (mMetadataLock) {
+ if ((programId != mSelectedAudioProgramId)
+ || (presentationId != mSelectedAudioPresentationId)) {
+ mSelectedAudioPresentationId = presentationId;
+ mSelectedAudioProgramId = programId;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Selects a track.
*
* @param type The type of the track to select. The type can be
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
old mode 100755
new mode 100644
index 15f511b..b769757
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -26,11 +26,13 @@
import android.app.ActivityManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.net.Uri;
import android.os.AsyncTask;
@@ -57,10 +59,8 @@
import android.view.WindowManager;
import android.view.accessibility.CaptioningManager;
import android.widget.FrameLayout;
-
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -171,7 +171,7 @@
@Override
public void createSession(InputChannel channel, ITvInputSessionCallback cb,
- String inputId, String sessionId) {
+ String inputId, String sessionId, AttributionSource tvAppAttributionSource) {
if (channel == null) {
Log.w(TAG, "Creating session without input channel");
}
@@ -183,6 +183,7 @@
args.arg2 = cb;
args.arg3 = inputId;
args.arg4 = sessionId;
+ args.arg5 = tvAppAttributionSource;
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION,
args).sendToTarget();
}
@@ -370,6 +371,24 @@
}
/**
+ * Returns a concrete implementation of {@link Session}.
+ *
+ * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager and
+ * needs to specify custom AttributionSource to AudioTrack, it needs to override this method to
+ * get the sessionId and AttrubutionSource passed. When no overriding, this method calls {@link
+ * #onCreateSession(String, String)} defaultly.
+ *
+ * @param inputId The ID of the TV input associated with the session.
+ * @param sessionId the unique sessionId created by TIF when session is created.
+ * @param tvAppAttributionSource The Attribution Source of the TV App.
+ */
+ @Nullable
+ public Session onCreateSession(@NonNull String inputId, @NonNull String sessionId,
+ @NonNull AttributionSource tvAppAttributionSource) {
+ return onCreateSession(inputId, sessionId);
+ }
+
+ /**
* Returns a concrete implementation of {@link RecordingSession}.
*
* <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager,
@@ -739,6 +758,74 @@
}
/**
+ * Sends an updated list of all audio presentations available from a Next Generation Audio
+ * service. This is used by the framework to maintain the audio presentation information for
+ * a given track of {@link TvTrackInfo#TYPE_AUDIO}, which in turn is used by
+ * {@link TvView#getAudioPresentations} for the application to retrieve metadata for the
+ * current audio track. The TV input service must call this method as soon as the audio
+ * track presentation information becomes available or is updated. Note that in a case
+ * where a part of the information for the current track is updated, it is not necessary
+ * to create a new {@link TvTrackInfo} object with a different track ID.
+ *
+ * @param audioPresentations A list of audio presentation information pertaining to the
+ * selected track.
+ */
+ public void notifyAudioPresentationChanged(@NonNull final List<AudioPresentation>
+ audioPresentations) {
+ final List<AudioPresentation> ap = new ArrayList<>(audioPresentations);
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAudioPresentationsChanged");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onAudioPresentationsChanged(ap);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in notifyAudioPresentationsChanged", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends the presentation and program IDs of the selected audio presentation. This is used
+ * to inform the application that a specific audio presentation is selected. The TV input
+ * service must call this method as soon as an audio presentation is selected either by
+ * default or in response to a call to {@link #onSelectTrack}. The selected audio
+ * presentation ID for a currently selected audio track is maintained in the framework until
+ * the next call to this method even after the entire audio presentation list for the track
+ * is updated (but is reset when the session is tuned to a new channel), so care must be
+ * taken not to result in an obsolete track audio presentation ID.
+ *
+ * @param presentationId The ID of the selected audio presentation for the current track.
+ * @param programId The ID of the program providing the selected audio presentation.
+ * @see #onSelectAudioPresentation
+ */
+ public void notifyAudioPresentationSelected(final int presentationId, final int programId) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAudioPresentationSelected");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onAudioPresentationSelected(presentationId, programId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in notifyAudioPresentationSelected", e);
+ }
+ }
+ });
+ }
+
+
+ /**
* Informs the application that the user is allowed to watch the current program content.
*
* <p>Each TV input service is required to query the system whether the user is allowed to
@@ -1128,7 +1215,7 @@
public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
/**
- * called when broadcast info is requested.
+ * Called when broadcast info is requested.
*
* @param request broadcast info request
*/
@@ -1136,7 +1223,7 @@
}
/**
- * called when broadcast info is removed.
+ * Called when broadcast info is removed.
*/
public void onRemoveBroadcastInfo(int requestId) {
}
@@ -1246,6 +1333,23 @@
}
/**
+ * Selects an audio presentation.
+ *
+ * <p>On successfully selecting the audio presentation,
+ * {@link #notifyAudioPresentationSelected} is invoked to provide updated information about
+ * the selected audio presentation to applications.
+ *
+ * @param presentationId The ID of the audio presentation to select.
+ * @param programId The ID of the program providing the selected audio presentation.
+ * @return {@code true} if the audio presentation selection was successful,
+ * {@code false} otherwise.
+ * @see #notifyAudioPresentationSelected
+ */
+ public boolean onSelectAudioPresentation(int presentationId, int programId) {
+ return false;
+ }
+
+ /**
* Processes a private command sent from the application to the TV input. This can be used
* to provide domain-specific features that are only known between certain TV inputs and
* their clients.
@@ -1579,6 +1683,13 @@
}
/**
+ * Calls {@link #onSelectAudioPresentation}.
+ */
+ void selectAudioPresentation(int presentationId, int programId) {
+ onSelectAudioPresentation(presentationId, programId);
+ }
+
+ /**
* Calls {@link #onSelectTrack}.
*/
void selectTrack(int type, String trackId) {
@@ -2497,8 +2608,10 @@
ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
String inputId = (String) args.arg3;
String sessionId = (String) args.arg4;
+ AttributionSource tvAppAttributionSource = (AttributionSource) args.arg5;
args.recycle();
- Session sessionImpl = onCreateSession(inputId, sessionId);
+ Session sessionImpl =
+ onCreateSession(inputId, sessionId, tvAppAttributionSource);
if (sessionImpl == null) {
try {
// Failed to create a session.
@@ -2534,7 +2647,7 @@
proxySession.mServiceHandler = mServiceHandler;
TvInputManager manager = (TvInputManager) getSystemService(
Context.TV_INPUT_SERVICE);
- manager.createSession(hardwareInputId,
+ manager.createSession(hardwareInputId, tvAppAttributionSource,
proxySession.mHardwareSessionCallback, mServiceHandler);
} else {
SomeArgs someArgs = SomeArgs.obtain();
diff --git a/media/java/android/media/tv/TvRecordingInfo.java b/media/java/android/media/tv/TvRecordingInfo.java
index 8de42f3..60ceb83 100644
--- a/media/java/android/media/tv/TvRecordingInfo.java
+++ b/media/java/android/media/tv/TvRecordingInfo.java
@@ -17,6 +17,7 @@
package android.media.tv;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
@@ -25,9 +26,13 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.List;
+
/**
- @hide
+ * This class is used to describe the meta information for a TV recording. It can be retrieved by
+ * the {@link android.media.tv.interactive.TvInteractiveAppService} by using getTvRecordingInfo
+ * or getTvRecordingInfoList. It can then be updated to the TV app using setTvRecordingInfo.
*/
public final class TvRecordingInfo implements Parcelable {
/*
@@ -54,105 +59,256 @@
})
public @interface TvRecordingListType {}
+ public static final int SUNDAY = 1;
+ public static final int MONDAY = 1 << 1;
+ public static final int TUESDAY = 1 << 2;
+ public static final int WEDNESDAY = 1 << 3;
+ public static final int THURSDAY = 1 << 4;
+ public static final int FRIDAY = 1 << 5;
+ public static final int SATURDAY = 1 << 6;
+
+ /**
+ * The days of the week defined in {@link java.time.DayOfWeek} are not used here because they
+ * map to integers that increment by 1. The intended use case in {@link #getRepeatDays()} uses
+ * a bitfield, which requires integers that map to 2^n.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY})
+ public @interface DaysOfWeek {}
+
private String mRecordingId;
- private int mStartPadding;
- private int mEndPadding;
+ private long mStartPaddingMillis;
+ private long mEndPaddingMillis;
private int mRepeatDays;
private String mName;
private String mDescription;
- private int mScheduledStartTime;
- private int mScheduledDuration;
+ private long mScheduledStartTimeMillis;
+ private long mScheduledDurationMillis;
private Uri mChannelUri;
- private Uri mProgramId;
- private List<String> mParentalRatings;
- private String mRecordingUri;
- private int mRecordingStartTime;
- private int mRecordingDuration;
+ private Uri mProgramUri;
+ private List<TvContentRating> mContentRatings;
+ private Uri mRecordingUri;
+ private long mRecordingStartTimeMillis;
+ private long mRecordingDurationMillis;
public TvRecordingInfo(
- @NonNull String recordingId, @NonNull int startPadding, @NonNull int endPadding,
- @NonNull int repeatDays, @NonNull int scheduledStartTime,
- @NonNull int scheduledDuration, @NonNull Uri channelUri, @Nullable Uri programId,
- @NonNull List<String> parentalRatings, @NonNull String recordingUri,
- @NonNull int recordingStartTime, @NonNull int recordingDuration) {
+ @NonNull String recordingId, long startPadding, long endPadding, int repeatDays,
+ @NonNull String name, @NonNull String description, long scheduledStartTime,
+ long scheduledDuration, @NonNull Uri channelUri, @Nullable Uri programUri,
+ @NonNull List<TvContentRating> contentRatings, @Nullable Uri recordingUri,
+ long recordingStartTime, long recordingDuration) {
mRecordingId = recordingId;
- mStartPadding = startPadding;
- mEndPadding = endPadding;
+ mStartPaddingMillis = startPadding;
+ mEndPaddingMillis = endPadding;
mRepeatDays = repeatDays;
- mScheduledStartTime = scheduledStartTime;
- mScheduledDuration = scheduledDuration;
+ mName = name;
+ mDescription = description;
+ mScheduledStartTimeMillis = scheduledStartTime;
+ mScheduledDurationMillis = scheduledDuration;
mChannelUri = channelUri;
- mScheduledDuration = scheduledDuration;
- mChannelUri = channelUri;
- mProgramId = programId;
- mParentalRatings = parentalRatings;
+ mProgramUri = programUri;
+ mContentRatings = contentRatings;
mRecordingUri = recordingUri;
- mRecordingStartTime = recordingStartTime;
- mRecordingDuration = recordingDuration;
+ mRecordingStartTimeMillis = recordingStartTime;
+ mRecordingDurationMillis = recordingDuration;
}
+
+ /**
+ * Returns the ID of this recording. This ID is created and maintained by the TV app.
+ */
@NonNull
public String getRecordingId() {
return mRecordingId;
}
+
+ /**
+ * Returns the start padding duration of this recording in milliseconds since the epoch.
+ *
+ * <p> A positive value should cause the recording to start earlier than the specified time.
+ * This should cause the actual duration of the recording to increase. A negative value should
+ * cause the recording to start later than the specified time. This should cause the actual
+ * duration of the recording to decrease.
+ */
@NonNull
- public int getStartPadding() {
- return mStartPadding;
+ public long getStartPaddingMillis() {
+ return mStartPaddingMillis;
}
+
+ /**
+ * Returns the ending padding duration of this recording in milliseconds since the epoch.
+ *
+ * <p> A positive value should cause the recording to end later than the specified time.
+ * This should cause the actual duration of the recording to increase. A negative value should
+ * cause the recording to end earlier than the specified time. This should cause the actual
+ * duration of the recording to decrease.
+ */
@NonNull
- public int getEndPadding() {
- return mEndPadding;
+ public long getEndPaddingMillis() {
+ return mEndPaddingMillis;
}
+
+ /**
+ * Returns the days of the week for which this recording should be repeated for.
+ *
+ * <p> This information is represented in the form of a bitfield, with each bit
+ * representing the day which the recording should be repeated.
+ *
+ * <p> The bitfield corresponds to each day of the week with the following format:
+ * <ul>
+ * <li>{@link #SUNDAY} - 0x01 (00000001)</li>
+ * <li>{@link #MONDAY} - 0x02 (00000010)</li>
+ * <li>{@link #TUESDAY} - 0x04 (00000100)</li>
+ * <li>{@link #WEDNESDAY} - 0x08 (00001000)</li>
+ * <li>{@link #THURSDAY} - 0x10 (00010000)</li>
+ * <li>{@link #FRIDAY} - 0x20 (00100000)</li>
+ * <li>{@link #SATURDAY} - 0x40 (01000000)</li>
+ * </ul>
+ *
+ * <p> You can specify multiple days to repeat the recording by performing a bitwise 'OR' on the
+ * bitfield. For example, for a recording to repeat on Sunday and Mondays, this function should
+ * return 0x03 (00000011).
+ *
+ * <p> A value of 0x00 indicates that the recording will not be repeated.
+ *
+ * <p> This format comes from the <a href="
+ * https://www.oipf.tv/docs/OIPF-T1-R2_Specification-Volume-5-Declarative-Application-Environment-v2_3-2014-01-24.pdf
+ * ">Open IPTV Forum Release 2 Specification</a>. It is described in Volume 5, section 7.10.1.1.
+ */
@NonNull
+ @DaysOfWeek
public int getRepeatDays() {
return mRepeatDays;
}
+
+ /**
+ * Returns the name of the scheduled recording.
+ *
+ * <p> This is set with {@link TvRecordingInfo#setName(String)} and sent to tv app with
+ * {@link android.media.tv.interactive.TvInteractiveAppService.Session#setTvRecordingInfo(String, TvRecordingInfo)}
+ */
@NonNull
public String getName() {
return mName;
}
- @NonNull
- public void setName(String name) {
+
+ /**
+ * Sets the name of the scheduled recording.
+ *
+ * <p> Updates to the {@link TvRecordingInfo} can be sent to the TV app with
+ * {@link android.media.tv.interactive.TvInteractiveAppService.Session#setTvRecordingInfo(String, TvRecordingInfo)}
+ */
+ public void setName(@NonNull String name) {
mName = name;
}
+
+ /**
+ * Returns the description of the scheduled recording.
+ *
+ * <p> This is set with {@link TvRecordingInfo#setDescription(String)} and sent to tv app with
+ * {@link android.media.tv.interactive.TvInteractiveAppService.Session#setTvRecordingInfo(String, TvRecordingInfo)}
+ */
@NonNull
public String getDescription() {
return mDescription;
}
- @NonNull
- public void setDescription(String description) {
+
+ /**
+ * Sets the description of the scheduled recording.
+ *
+ * <p> Updates to the {@link TvRecordingInfo} can be sent to the TV app with
+ * {@link android.media.tv.interactive.TvInteractiveAppService.Session#setTvRecordingInfo(String, TvRecordingInfo)}
+ */
+ public void setDescription(@NonNull String description) {
mDescription = description;
}
+
+ /**
+ * Returns the scheduled start time of the recording in milliseconds since the epoch.
+ */
+ @IntRange(from = 0)
@NonNull
- public int getScheduledStartTime() {
- return mScheduledStartTime;
+ public long getScheduledStartTimeMillis() {
+ return mScheduledStartTimeMillis;
}
+
+ /**
+ * Returns the scheduled duration of the recording in milliseconds since the epoch.
+ */
+ @IntRange(from = 0)
@NonNull
- public int getScheduledDuration() {
- return mScheduledDuration;
+ public long getScheduledDurationMillis() {
+ return mScheduledDurationMillis;
}
+
+ /**
+ * Returns the uri of the broadcast channel where the recording will take place.
+ */
@NonNull
public Uri getChannelUri() {
return mChannelUri;
}
+
+ /**
+ * Returns the uri of the scheduled program or series.
+ *
+ * <p> For recordings scheduled using scheduleRecording, this value may be null. A non-null
+ * programUri implies the started recording should be of that specific program, whereas a null
+ * programUri does not impose such a requirement and the recording can span across
+ * multiple TV programs.
+ */
@Nullable
- public Uri getProgramId() {
- return mProgramId;
+ public Uri getProgramUri() {
+ return mProgramUri;
}
+
+ /**
+ * Returns a list of content ratings for the program(s) in this recording.
+ *
+ * <p> Returns an empty list if no content rating information is available.
+ */
@NonNull
- public List<String> getParentalRatings() {
- return mParentalRatings;
+ public List<TvContentRating> getContentRatings() {
+ return mContentRatings;
}
- @NonNull
- public String getRecordingUri() {
+
+ /**
+ * The uri of the recording in local storage.
+ *
+ * <p> Could be null in the event that the recording has not been completed.
+ */
+ @Nullable
+ public Uri getRecordingUri() {
return mRecordingUri;
}
+
+ /**
+ * The real start time of the recording, including any padding, in milliseconds since the epoch.
+ *
+ * <p> This may not be the same as the value of {@link #getScheduledStartTimeMillis()} due to a
+ * recording starting late, or due to start/end padding.
+ *
+ * <p> Returns -1 for recordings that have not yet started.
+ */
+ @IntRange(from = -1)
@NonNull
- public int getRecordingStartTime() {
- return mRecordingStartTime;
+ public long getRecordingStartTimeMillis() {
+ return mRecordingStartTimeMillis;
}
+
+ /**
+ * The real duration of the recording, including any padding, in milliseconds since the epoch.
+ *
+ * <p> This may not be the same as the value of {@link #getScheduledDurationMillis()} due to a
+ * recording starting late, or due to start/end padding.
+ *
+ * <p> Returns -1 for recordings that have not yet started.
+ */
+ @IntRange(from = -1)
@NonNull
- public int getRecordingDuration() {
- return mRecordingDuration;
+ public long getRecordingDurationMillis() {
+ return mRecordingDurationMillis;
}
@Override
@@ -169,36 +325,42 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mRecordingId);
- dest.writeInt(mStartPadding);
- dest.writeInt(mEndPadding);
+ dest.writeLong(mStartPaddingMillis);
+ dest.writeLong(mEndPaddingMillis);
dest.writeInt(mRepeatDays);
dest.writeString(mName);
dest.writeString(mDescription);
- dest.writeInt(mScheduledStartTime);
- dest.writeInt(mScheduledDuration);
+ dest.writeLong(mScheduledStartTimeMillis);
+ dest.writeLong(mScheduledDurationMillis);
dest.writeString(mChannelUri == null ? null : mChannelUri.toString());
- dest.writeString(mProgramId == null ? null : mProgramId.toString());
- dest.writeStringList(mParentalRatings);
- dest.writeString(mRecordingUri);
- dest.writeInt(mRecordingDuration);
- dest.writeInt(mRecordingStartTime);
+ dest.writeString(mProgramUri == null ? null : mProgramUri.toString());
+ List<String> flattenedContentRatings = new ArrayList<String>();
+ mContentRatings.forEach((rating) -> flattenedContentRatings.add(rating.flattenToString()));
+ dest.writeList(mContentRatings);
+ dest.writeString(mRecordingUri == null ? null : mProgramUri.toString());
+ dest.writeLong(mRecordingDurationMillis);
+ dest.writeLong(mRecordingStartTimeMillis);
}
private TvRecordingInfo(Parcel in) {
mRecordingId = in.readString();
- mStartPadding = in.readInt();
- mEndPadding = in.readInt();
+ mStartPaddingMillis = in.readLong();
+ mEndPaddingMillis = in.readLong();
mRepeatDays = in.readInt();
mName = in.readString();
mDescription = in.readString();
- mScheduledStartTime = in.readInt();
- mScheduledDuration = in.readInt();
+ mScheduledStartTimeMillis = in.readLong();
+ mScheduledDurationMillis = in.readLong();
mChannelUri = Uri.parse(in.readString());
- mProgramId = Uri.parse(in.readString());
- in.readStringList(mParentalRatings);
- mRecordingUri = in.readString();
- mRecordingDuration = in.readInt();
- mRecordingStartTime = in.readInt();
+ mProgramUri = Uri.parse(in.readString());
+ mContentRatings = new ArrayList<TvContentRating>();
+ List<String> flattenedContentRatings = new ArrayList<String>();
+ in.readStringList(flattenedContentRatings);
+ flattenedContentRatings.forEach((rating) ->
+ mContentRatings.add(TvContentRating.unflattenFromString(rating)));
+ mRecordingUri = Uri.parse(in.readString());
+ mRecordingDurationMillis = in.readLong();
+ mRecordingStartTimeMillis = in.readLong();
}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 2fdbc3b..c7a63ac 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -30,6 +31,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
@@ -51,9 +53,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
-
import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
@@ -111,6 +113,7 @@
private int mSurfaceViewTop;
private int mSurfaceViewBottom;
private TimeShiftPositionCallback mTimeShiftPositionCallback;
+ private AttributionSource mTvAppAttributionSource;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
@@ -185,6 +188,7 @@
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ mTvAppAttributionSource = getContext().getAttributionSource();
}
/**
@@ -304,6 +308,22 @@
}
/**
+ * Override default attribution source of TV App.
+ *
+ * <p>An attribution source of TV App is used to attribute work to TV Input Service.
+ * The default attribution source is created by {@link Context#getAttributionSource()}.
+ * Call this method before calling {@link #tune(String, Uri, Bundle)} or {@link
+ * #timeShiftPlay(String, Uri)} to override the default attribution source.
+ *
+ * @param tvAppAttributionSource The attribution source of the TV App.
+ */
+ public void overrideTvAppAttributionSource(@NonNull AttributionSource tvAppAttributionSource) {
+ if (tvAppAttributionSource != null) {
+ mTvAppAttributionSource = tvAppAttributionSource;
+ }
+ }
+
+ /**
* Tunes to a given channel.
*
* @param inputId The ID of the TV input for the given channel.
@@ -355,7 +375,8 @@
// is obsolete and should ignore it.
mSessionCallback = new MySessionCallback(inputId, channelUri, params);
if (mTvInputManager != null) {
- mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+ mTvInputManager.createSession(
+ inputId, mTvAppAttributionSource, mSessionCallback, mHandler);
}
}
}
@@ -434,6 +455,35 @@
}
/**
+ * Selects an audio presentation.
+ *
+ * @param presentationId The ID of the audio presentation to select.
+ * @param programId The ID of the program providing the selected audio presentation.
+ * @see #getAudioPresentations
+ */
+ public void selectAudioPresentation(int presentationId, int programId) {
+ if (mSession != null) {
+ mSession.selectAudioPresentation(presentationId, programId);
+ }
+ }
+
+ /**
+ * Returns the list of audio presentations from the selected track of type
+ * {@link TvTrackInfo#TYPE_AUDIO}.
+ *
+ * @return the list of audio presentations from the selected audio track, or an empty list if no
+ * audio presentations are available.
+ * @see #selectAudioPresentation
+ */
+ @NonNull
+ public List<AudioPresentation> getAudioPresentations() {
+ if (mSession == null) {
+ return new ArrayList<AudioPresentation>();
+ }
+ return mSession.getAudioPresentations();
+ }
+
+ /**
* Selects a track.
*
* @param type The type of the track to select. The type can be {@link TvTrackInfo#TYPE_AUDIO},
@@ -526,7 +576,8 @@
resetInternal();
mSessionCallback = new MySessionCallback(inputId, recordedProgramUri);
if (mTvInputManager != null) {
- mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+ mTvInputManager.createSession(
+ inputId, mTvAppAttributionSource, mSessionCallback, mHandler);
}
}
}
@@ -977,6 +1028,27 @@
}
/**
+ * This is called when the audio presentation information has been changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param audioPresentations A list of updated audio presentation information.
+ */
+ public void onAudioPresentationsChanged(@NonNull String inputId,
+ @NonNull List<AudioPresentation> audioPresentations) {
+ }
+
+ /**
+ * This is called when audio presentation selection has changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param presentationId The ID of the audio presentation selected.
+ * @param programId The ID of the program providing the selected audio presentation.
+ */
+ public void onAudioPresentationSelected(@NonNull String inputId, int presentationId,
+ int programId) {
+ }
+
+ /**
* This is called when the track information has been changed.
*
* @param inputId The ID of the TV input bound to this view.
@@ -1242,6 +1314,37 @@
}
@Override
+ public void onAudioPresentationsChanged(Session session,
+ List<AudioPresentation> audioPresentations) {
+ if (DEBUG) {
+ Log.d(TAG, "onAudioPresentationsChanged(" + audioPresentations + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onAudioPresentationsChanged - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onAudioPresentationsChanged(mInputId, audioPresentations);
+ }
+ }
+
+ @Override
+ public void onAudioPresentationSelected(Session session, int presentationId,
+ int programId) {
+ if (DEBUG) {
+ Log.d(TAG, "onAudioPresentationSelected(presentationId=" + presentationId
+ + ", programId=" + programId + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onAudioPresentationSelected - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onAudioPresentationSelected(mInputId, presentationId, programId);
+ }
+ }
+
+ @Override
public void onTracksChanged(Session session, List<TvTrackInfo> tracks) {
if (DEBUG) {
Log.d(TAG, "onTracksChanged(" + tracks + ")");
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index be2c16c..d1777cc 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -1219,13 +1219,15 @@
}
/**
- * Sets the recording info for the specified recording
+ * Sets the recording info for the specified recording.
*
- * @hide
+ * @param recordingId The ID of the recording to set the info for. This is provided by the
+ * TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String)}
+ * @param recordingInfo The {@link TvRecordingInfo} to set to the recording.
*/
@CallSuper
- public void setTvRecordingInfo(@NonNull String recordingId,
- @NonNull TvRecordingInfo recordingInfo) {
+ public void setTvRecordingInfo(
+ @NonNull String recordingId, @NonNull TvRecordingInfo recordingInfo) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
@@ -1242,8 +1244,8 @@
/**
* Gets the recording info for the specified recording
- *
- * @hide
+ * @param recordingId The ID of the recording to set the info for. This is provided by the
+ * TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String)}
*/
@CallSuper
public void requestTvRecordingInfo(@NonNull String recordingId) {
@@ -1262,9 +1264,9 @@
}
/**
- * Gets the recording info list for the specified recording type
+ * Gets a list of {@link TvRecordingInfo} for the specified recording type.
*
- * @hide
+ * @param type The type of recording to retrieve.
*/
@CallSuper
public void requestTvRecordingInfoList(@NonNull @TvRecordingInfo.TvRecordingListType
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index af03bb8..4a01440 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -1055,6 +1055,47 @@
@NonNull String algorithm, @NonNull String alias, @NonNull byte[] data) {
}
+ /**
+ * This is called when {@link TvInteractiveAppService.Session#setTvRecordingInfo(String,
+ * TvRecordingInfo)} is called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param recordingId The ID of the recording to set the info for. This is provided by the
+ * TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String)}
+ * @param recordingInfo The {@link TvRecordingInfo} to set to the recording.
+ */
+ public void onSetTvRecordingInfo(
+ @NonNull String iAppServiceId,
+ @NonNull String recordingId,
+ @NonNull TvRecordingInfo recordingInfo) {
+ }
+
+ /**
+ * This is called when
+ * {@link TvInteractiveAppService.Session#requestTvRecordingInfo(String)} is
+ * called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param recordingId The ID of the recording to get the info for. This is provided by the
+ * TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String)}
+ */
+ public void onRequestTvRecordingInfo(
+ @NonNull String iAppServiceId,
+ @NonNull String recordingId) {
+ }
+
+ /**
+ * This is called when
+ * {@link TvInteractiveAppService.Session#requestTvRecordingInfoList(int)} is
+ * called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param type The type of recording requested to retrieve.
+ */
+ public void onRequestTvRecordingInfoList(
+ @NonNull String iAppServiceId,
+ @NonNull @TvRecordingInfo.TvRecordingListType int type) {
+ }
}
/**
@@ -1440,6 +1481,51 @@
}
@Override
+ public void onSetTvRecordingInfo(
+ Session session, String recordingId, TvRecordingInfo recordingInfo) {
+ if (DEBUG) {
+ Log.d(TAG, "onSetRecordingInfo");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onSetRecordingInfo - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onSetTvRecordingInfo(mIAppServiceId, recordingId, recordingInfo);
+ }
+ }
+
+ @Override
+ public void onRequestTvRecordingInfo(Session session,
+ String recordingId) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestRecordingInfo");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestRecordingInfo - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onRequestTvRecordingInfo(mIAppServiceId, recordingId);
+ }
+ }
+
+ @Override
+ public void onRequestTvRecordingInfoList(Session session,
+ int type) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestRecordingInfoList");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestRecordingInfoList - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onRequestTvRecordingInfoList(mIAppServiceId, type);
+ }
+ }
+
+ @Override
public void onRequestSigning(
Session session, String id, String algorithm, String alias, byte[] data) {
if (DEBUG) {
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 27c2a98..7d08b81 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1170,6 +1170,11 @@
* in Tuner 2.0 or higher version. Unsupported version will cause no-op. Use {@link
* TunerVersionChecker#getTunerVersion()} to get the version information.
*
+ * <p>Tuning with {@link
+ * android.media.tv.tuner.frontend.IptvFrontendSettings} is only supported
+ * in Tuner 3.0 or higher version. Unsupported version will cause no-op. Use {@link
+ * TunerVersionChecker#getTunerVersion()} to get the version information.
+ *
* @param settings Signal delivery information the frontend uses to
* search and lock the signal.
* @return result status of tune operation.
@@ -1198,6 +1203,12 @@
return RESULT_UNAVAILABLE;
}
}
+ if (mFrontendType == FrontendSettings.TYPE_IPTV) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "Tuner with IPTV Frontend")) {
+ return RESULT_UNAVAILABLE;
+ }
+ }
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
mFrontendInfo = null;
@@ -1286,6 +1297,13 @@
return RESULT_UNAVAILABLE;
}
}
+ if (mFrontendType == FrontendSettings.TYPE_IPTV) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0,
+ "Tuner with IPTV Frontend")) {
+ return RESULT_UNAVAILABLE;
+ }
+ }
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
mFrontendLock)) {
mScanCallback = scanCallback;
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 2f45a70..0a1ecee 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -38,7 +38,7 @@
/** @hide */
@IntDef(prefix = "TYPE_",
value = {TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS,
- TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB})
+ TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB, TYPE_IPTV})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
@@ -86,7 +86,10 @@
* Digital Terrestrial Multimedia Broadcast standard (DTMB) frontend type.
*/
public static final int TYPE_DTMB = FrontendType.DTMB;
-
+ /**
+ * Internet Protocol (IPTV) frontend type.
+ */
+ public static final int TYPE_IPTV = FrontendType.IPTV;
/** @hide */
@LongDef(prefix = "FEC_",
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 9fbea72..fd677ac 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -23,6 +23,7 @@
import android.annotation.SystemApi;
import android.media.tv.tuner.Lnb;
import android.media.tv.tuner.TunerVersionChecker;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -57,7 +58,10 @@
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_IDS,
- FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO})
+ FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO,
+ FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL, FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST,
+ FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED, FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS,
+ FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -271,6 +275,36 @@
android.hardware.tv.tuner.FrontendStatusType.DVBT_CELL_IDS;
/**
+ * IPTV content URL.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_CONTENT_URL;
+
+ /**
+ * IPTV packets lost.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_PACKETS_LOST;
+
+ /**
+ * IPTV packets received.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_PACKETS_RECEIVED;
+
+ /**
+ * IPTV worst jitter.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_WORST_JITTER_MS;
+
+ /**
+ * IPTV average jitter.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_AVERAGE_JITTER_MS;
+
+ /**
* All PLP information in a frequency band for ATSC-3.0 frontend, which includes both tuned and
* not tuned PLPs for currently watching service.
*/
@@ -519,6 +553,11 @@
private int[] mStreamIds;
private int[] mDvbtCellIds;
private Atsc3PlpInfo[] mAllPlpInfo;
+ private String mIptvContentUrl;
+ private Long mIptvPacketsLost;
+ private Long mIptvPacketsReceived;
+ private Integer mIptvWorstJitterMs;
+ private Integer mIptvAverageJitterMs;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -1144,4 +1183,94 @@
return mUec;
}
}
+
+ /**
+ * Gets the IPTV content URL.
+ *
+ * @return A String URL in the format protocol://ip:port (udp://127.0.0.1:3000).
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @NonNull
+ public String getIptvContentUrl() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvContentUrl status");
+ if (mIptvContentUrl == null) {
+ throw new IllegalStateException("IptvContentUrl status is empty");
+ }
+ return mIptvContentUrl;
+ }
+
+ /**
+ * Gets the number of packets lost.
+ *
+ * @return A long value representing the number of packets lost in transmission.
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public long getIptvPacketsLost() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvPacketsLost status");
+ if (mIptvPacketsLost == null) {
+ throw new IllegalStateException("IptvPacketsLost status is empty");
+ }
+ return mIptvPacketsLost;
+ }
+
+ /**
+ * Gets the number of packets received.
+ *
+ * @return A long value representing the number of packets received.
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public long getIptvPacketsReceived() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvPacketsReceived status");
+ if (mIptvPacketsReceived == null) {
+ throw new IllegalStateException("IptvPacketsReceived status is empty");
+ }
+ return mIptvPacketsReceived;
+ }
+
+ /**
+ * Gets the worst jitter.
+ *
+ * @return An integer representing worst jitter recorded (in milliseconds).
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public int getIptvWorstJitterMillis() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvWorstJitterMs status");
+ if (mIptvWorstJitterMs == null) {
+ throw new IllegalStateException("IptvWorstJitterMs status is empty");
+ }
+ return mIptvWorstJitterMs;
+ }
+
+ /**
+ * Gets the average jitter.
+ *
+ * @return An integer representing average jitter recorded (in milliseconds).
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public int getIptvAverageJitterMillis() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvAverageJitterMs status");
+ if (mIptvAverageJitterMs == null) {
+ throw new IllegalStateException("IptvAverageJitterMs status is empty");
+ }
+ return mIptvAverageJitterMs;
+ }
}
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
new file mode 100644
index 0000000..fb11ea9
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Size;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.FrontendIptvSettingsIgmp;
+import android.hardware.tv.tuner.FrontendIptvSettingsProtocol;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Frontend settings for IPTV.
+ *
+ * @hide
+ */
+@SystemApi
+public class IptvFrontendSettings extends FrontendSettings {
+ /** @hide */
+ @IntDef(prefix = "PROTOCOL_",
+ value = {PROTOCOL_UNDEFINED, PROTOCOL_UDP, PROTOCOL_RTP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Protocol {}
+
+ /**
+ * IP protocol type UNDEFINED.
+ */
+ public static final int PROTOCOL_UNDEFINED = FrontendIptvSettingsProtocol.UNDEFINED;
+
+ /**
+ * IP protocol type UDP (User Datagram Protocol).
+ */
+ public static final int PROTOCOL_UDP = FrontendIptvSettingsProtocol.UDP;
+
+ /**
+ * IP protocol type RTP (Real-time Transport Protocol).
+ */
+ public static final int PROTOCOL_RTP = FrontendIptvSettingsProtocol.RTP;
+
+ /** @hide */
+ @IntDef(prefix = "IGMP_",
+ value = {IGMP_UNDEFINED, IGMP_V1, IGMP_V2, IGMP_V3})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Igmp {}
+
+ /**
+ * IGMP (Internet Group Management Protocol) UNDEFINED.
+ */
+ public static final int IGMP_UNDEFINED = FrontendIptvSettingsIgmp.UNDEFINED;
+
+ /**
+ * IGMP (Internet Group Management Protocol) V1.
+ */
+ public static final int IGMP_V1 = FrontendIptvSettingsIgmp.V1;
+
+ /**
+ * IGMP (Internet Group Management Protocol) V2.
+ */
+ public static final int IGMP_V2 = FrontendIptvSettingsIgmp.V2;
+
+ /**
+ * IGMP (Internet Group Management Protocol) V3.
+ */
+ public static final int IGMP_V3 = FrontendIptvSettingsIgmp.V3;
+
+ private final byte[] mSrcIpAddress;
+ private final byte[] mDstIpAddress;
+ private final int mSrcPort;
+ private final int mDstPort;
+ private final IptvFrontendSettingsFec mFec;
+ private final int mProtocol;
+ private final int mIgmp;
+ private final long mBitrate;
+ private final String mContentUrl;
+
+ public IptvFrontendSettings(@NonNull byte[] srcIpAddress, @NonNull byte[] dstIpAddress,
+ int srcPort, int dstPort, @NonNull IptvFrontendSettingsFec fec, int protocol, int igmp,
+ long bitrate, @NonNull String contentUrl) {
+ super(0);
+ mSrcIpAddress = srcIpAddress;
+ mDstIpAddress = dstIpAddress;
+ mSrcPort = srcPort;
+ mDstPort = dstPort;
+ mFec = fec;
+ mProtocol = protocol;
+ mIgmp = igmp;
+ mBitrate = bitrate;
+ mContentUrl = contentUrl;
+ }
+
+ /**
+ * Gets the source IP address.
+ */
+ @Size(min = 4, max = 16)
+ @NonNull
+ public byte[] getSrcIpAddress() {
+ return mSrcIpAddress;
+ }
+
+ /**
+ * Gets the destination IP address.
+ */
+ @Size(min = 4, max = 16)
+ @NonNull
+ public byte[] getDstIpAddress() {
+ return mDstIpAddress;
+ }
+
+ /**
+ * Gets the source port.
+ */
+ public int getSrcPort() {
+ return mSrcPort;
+ }
+
+ /**
+ * Gets the destination port.
+ */
+ public int getDstPort() {
+ return mDstPort;
+ }
+
+ /**
+ * Gets FEC (Forward Error Correction).
+ */
+ @Nullable
+ public IptvFrontendSettingsFec getFec() {
+ return mFec;
+ }
+
+ /**
+ * Gets the protocol.
+ */
+ @Protocol
+ public int getProtocol() {
+ return mProtocol;
+ }
+
+ /**
+ * Gets the IGMP (Internet Group Management Protocol).
+ */
+ @Igmp
+ public int getIgmp() {
+ return mIgmp;
+ }
+
+ /**
+ * Gets the bitrate.
+ */
+ @IntRange(from = 0)
+ public long getBitrate() {
+ return mBitrate;
+ }
+
+ /**
+ * Gets the contentUrl
+ * contentUrl is a source URL in the format protocol://ip:port containing data
+ */
+ @NonNull
+ public String getContentUrl() {
+ return mContentUrl;
+ }
+
+ /**
+ * Creates a builder for {@link IptvFrontendSettings}.
+ */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link IptvFrontendSettings}.
+ */
+ public static final class Builder {
+ private byte[] mSrcIpAddress = {0, 0, 0, 0};
+ private byte[] mDstIpAddress = {0, 0, 0, 0};
+ private int mSrcPort = 0;
+ private int mDstPort = 0;
+ private IptvFrontendSettingsFec mFec = null;
+ private int mProtocol = FrontendIptvSettingsProtocol.UNDEFINED;
+ private int mIgmp = FrontendIptvSettingsIgmp.UNDEFINED;
+ private long mBitrate = 0;
+ private String mContentUrl = "";
+
+ private Builder() {
+ }
+
+ /**
+ * Sets the source IP address.
+ *
+ * <p>Default value is 0.0.0.0, an invalid IP address.
+ */
+ @NonNull
+ public Builder setSrcIpAddress(@NonNull byte[] srcIpAddress) {
+ mSrcIpAddress = srcIpAddress;
+ return this;
+ }
+
+ /**
+ * Sets the destination IP address.
+ *
+ * <p>Default value is 0.0.0.0, an invalid IP address.
+ */
+ @NonNull
+ public Builder setDstIpAddress(@NonNull byte[] dstIpAddress) {
+ mDstIpAddress = dstIpAddress;
+ return this;
+ }
+
+ /**
+ * Sets the source IP port.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ public Builder setSrcPort(int srcPort) {
+ mSrcPort = srcPort;
+ return this;
+ }
+
+ /**
+ * Sets the destination IP port.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ public Builder setDstPort(int dstPort) {
+ mDstPort = dstPort;
+ return this;
+ }
+
+ /**
+ * Sets the FEC (Forward Error Correction).
+ *
+ * <p>Default value is {@code null}.
+ */
+ @NonNull
+ public Builder setFec(@Nullable IptvFrontendSettingsFec fec) {
+ mFec = fec;
+ return this;
+ }
+
+ /**
+ * Sets the protocol.
+ *
+ * <p>Default value is {@link #PROTOCOL_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setProtocol(@Protocol int protocol) {
+ mProtocol = protocol;
+ return this;
+ }
+
+ /**
+ * Sets the IGMP (Internet Group Management Protocol).
+ *
+ * <p>Default value is {@link #IGMP_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setIgmp(@Igmp int igmp) {
+ mIgmp = igmp;
+ return this;
+ }
+
+ /**
+ * Sets the bitrate.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ public Builder setBitrate(@IntRange(from = 0) long bitrate) {
+ mBitrate = bitrate;
+ return this;
+ }
+
+ /**
+ * Sets the contentUrl.
+ *
+ * <p>Default value is "".
+ */
+ @NonNull
+ public Builder setContentUrl(@NonNull String contentUrl) {
+ mContentUrl = contentUrl;
+ return this;
+ }
+
+ /**
+ * Builds a {@link IptvFrontendSettings} object.
+ */
+ @NonNull
+ public IptvFrontendSettings build() {
+ return new IptvFrontendSettings(mSrcIpAddress, mDstIpAddress, mSrcPort,
+ mDstPort, mFec, mProtocol, mIgmp, mBitrate, mContentUrl);
+ }
+ }
+
+ @Override
+ public int getType() {
+ return FrontendSettings.TYPE_IPTV;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
new file mode 100644
index 0000000..699d615
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.FrontendIptvSettingsFecType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * FEC (Forward Error Correction) for IPTV.
+ *
+ * @hide
+ */
+@SystemApi
+public class IptvFrontendSettingsFec {
+ /** @hide */
+ @IntDef(prefix = "FEC_TYPE_",
+ value = {FEC_TYPE_UNDEFINED, FEC_TYPE_COLUMN, FEC_TYPE_ROW, FEC_TYPE_COLUMN_ROW})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FecType {}
+
+ /**
+ * FEC (Forward Error Correction) type UNDEFINED.
+ */
+ public static final int FEC_TYPE_UNDEFINED = FrontendIptvSettingsFecType.UNDEFINED;
+
+ /**
+ * FEC (Forward Error Correction) type Column.
+ */
+ public static final int FEC_TYPE_COLUMN = FrontendIptvSettingsFecType.COLUMN;
+
+ /**
+ * FEC (Forward Error Correction) type ROW.
+ */
+ public static final int FEC_TYPE_ROW = FrontendIptvSettingsFecType.ROW;
+
+ /**
+ * FEC (Forward Error Correction) type Column Row.
+ */
+ public static final int FEC_TYPE_COLUMN_ROW = FrontendIptvSettingsFecType.COLUMN_ROW;
+
+ private final int mFecType;
+ private final int mFecRowNum;
+ private final int mFecColNum;
+
+ public IptvFrontendSettingsFec(@FecType int fecType, int fecRowNum, int fecColNum) {
+ mFecType = fecType;
+ mFecRowNum = fecRowNum;
+ mFecColNum = fecColNum;
+ }
+
+ /**
+ * Gets the FEC (Forward Error Correction) type.
+ */
+ @FecType
+ public int getFecType() {
+ return mFecType;
+ }
+
+ /**
+ * Get the FEC (Forward Error Correction) row number.
+ */
+ @IntRange(from = 0)
+ public int getFecRowNum() {
+ return mFecRowNum;
+ }
+
+ /**
+ * Gets the FEC (Forward Error Correction) column number.
+ */
+ @IntRange(from = 0)
+ public int getFecColNum() {
+ return mFecColNum;
+ }
+
+ /**
+ * Creates a builder for {@link IptvFrontendSettingsFec}.
+ */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link IptvFrontendSettingsFec}.
+ */
+ public static final class Builder {
+ private int mFecType;
+ private int mFecRowNum;
+ private int mFecColNum;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets the FEC (Forward Error Correction) type
+ */
+ @NonNull
+ public Builder setFecType(@FecType int fecType) {
+ mFecType = fecType;
+ return this;
+ }
+ /**
+ * Sets the FEC (Forward Error Correction) row number.
+ */
+ @NonNull
+ public Builder setFecRowNum(@IntRange(from = 0) int fecRowNum) {
+ mFecRowNum = fecRowNum;
+ return this;
+ }
+ /**
+ * Sets the FEC (Forward Error Correction) column number.
+ */
+ @NonNull
+ public Builder setFecColNum(@IntRange(from = 0) int fecColNum) {
+ mFecColNum = fecColNum;
+ return this;
+ }
+
+ /**
+ * Builds a {@link IptvFrontendSettingsFec} object.
+ */
+ @NonNull
+ public IptvFrontendSettingsFec build() {
+ return new IptvFrontendSettingsFec(mFecType, mFecRowNum, mFecColNum);
+ }
+ }
+}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 681d76a..b70818d 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1021,9 +1021,10 @@
static jbyteArray android_media_MediaDrm_getSupportedCryptoSchemesNative(JNIEnv *env) {
sp<IDrm> drm = android::DrmUtils::MakeDrm();
+ if (drm == NULL) return env->NewByteArray(0);
+
std::vector<uint8_t> bv;
drm->getSupportedSchemes(bv);
-
jbyteArray jUuidBytes = env->NewByteArray(bv.size());
env->SetByteArrayRegion(jUuidBytes, 0, bv.size(), reinterpret_cast<const jbyte *>(bv.data()));
return jUuidBytes;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 1a41bc2..625e842 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -132,6 +132,11 @@
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtPartialReceptionFlag.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtSettings.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtTimeInterleaveMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsFec.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsProtocol.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsIgmp.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsFecType.h>
#include <aidl/android/hardware/tv/tuner/FrontendModulation.h>
#include <aidl/android/hardware/tv/tuner/FrontendModulationStatus.h>
#include <aidl/android/hardware/tv/tuner/FrontendRollOff.h>
@@ -283,6 +288,11 @@
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtPartialReceptionFlag;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtSettings;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtTimeInterleaveMode;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsFec;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsProtocol;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsIgmp;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsFecType;
using ::aidl::android::hardware::tv::tuner::FrontendModulation;
using ::aidl::android::hardware::tv::tuner::FrontendModulationStatus;
using ::aidl::android::hardware::tv::tuner::FrontendRollOff;
@@ -2827,6 +2837,52 @@
env->DeleteLocalRef(plpClazz);
break;
}
+ case FrontendStatus::Tag::iptvContentUrl: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvContentUrl", "Ljava/lang/String;");
+ std::string iptvContentUrl = s.get<FrontendStatus::Tag::iptvContentUrl>();
+ jstring iptvContentUrlUtf8 = env->NewStringUTF(iptvContentUrl.c_str());
+ env->SetObjectField(statusObj, field, iptvContentUrlUtf8);
+ env->DeleteLocalRef(iptvContentUrlUtf8);
+ break;
+ }
+ case FrontendStatus::Tag::iptvPacketsLost: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvPacketsLost", "Ljava/lang/Long;");
+ jobject newLongObj =
+ env->NewObject(longClazz, initLong,
+ s.get<FrontendStatus::Tag::iptvPacketsLost>());
+ env->SetObjectField(statusObj, field, newLongObj);
+ env->DeleteLocalRef(newLongObj);
+ break;
+ }
+ case FrontendStatus::Tag::iptvPacketsReceived: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvPacketsReceived", "Ljava/lang/Long;");
+ jobject newLongObj =
+ env->NewObject(longClazz, initLong,
+ s.get<FrontendStatus::Tag::iptvPacketsReceived>());
+ env->SetObjectField(statusObj, field, newLongObj);
+ env->DeleteLocalRef(newLongObj);
+ break;
+ }
+ case FrontendStatus::Tag::iptvWorstJitterMs: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvWorstJitterMs",
+ "Ljava/lang/Integer;");
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::iptvWorstJitterMs>());
+ env->SetObjectField(statusObj, field, newIntegerObj);
+ env->DeleteLocalRef(newIntegerObj);
+ break;
+ }
+ case FrontendStatus::Tag::iptvAverageJitterMs: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvAverageJitterMs",
+ "Ljava/lang/Integer;");
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::iptvAverageJitterMs>());
+ env->SetObjectField(statusObj, field, newIntegerObj);
+ env->DeleteLocalRef(newIntegerObj);
+ break;
+ }
}
}
return statusObj;
@@ -3384,6 +3440,106 @@
return frontendSettings;
}
+static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config, const char* className) {
+ jclass clazz = env->FindClass(className);
+
+ jbyteArray jsrcIpAddress = static_cast<jbyteArray>(
+ env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B")));
+ jsize srcSize = env->GetArrayLength(jsrcIpAddress);
+ jbyteArray jdstIpAddress = static_cast<jbyteArray>(
+ env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B")));
+ jsize dstSize = env->GetArrayLength(jdstIpAddress);
+
+ DemuxIpAddress res;
+
+ if (srcSize != dstSize) {
+ // should never happen. Validated on Java size.
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize);
+ return res;
+ }
+
+ if (srcSize == IP_V4_LENGTH) {
+ vector<uint8_t> srcAddr;
+ vector<uint8_t> dstAddr;
+ srcAddr.resize(IP_V4_LENGTH);
+ dstAddr.resize(IP_V4_LENGTH);
+ env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
+ env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
+ res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(srcAddr);
+ res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(dstAddr);
+ } else if (srcSize == IP_V6_LENGTH) {
+ vector<uint8_t> srcAddr;
+ vector<uint8_t> dstAddr;
+ srcAddr.resize(IP_V6_LENGTH);
+ dstAddr.resize(IP_V6_LENGTH);
+ env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
+ env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
+ res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(srcAddr);
+ res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(dstAddr);
+ } else {
+ // should never happen. Validated on Java size.
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Invalid IP address length %d", srcSize);
+ return res;
+ }
+
+ res.srcPort = env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I"));
+ res.dstPort = env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I"));
+
+ return res;
+}
+
+static FrontendIptvSettingsFec getIptvFrontendSettingsFec(JNIEnv *env, const jobject &settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettings");
+ jobject fec = env->GetObjectField(settings, env->GetFieldID(clazz, "mFec",
+ "[Landroid/media/tv/tuner/frontend/IptvFrontendSettingsFec;"));
+ jclass fecClazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettingsFec");
+ FrontendIptvSettingsFecType fecType =
+ static_cast<FrontendIptvSettingsFecType>(
+ env->GetIntField(fec, env->GetFieldID(fecClazz, "mFec", "I")));
+ int32_t fecColNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecColNum", "I"));
+ int32_t fecRowNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecRowNum", "I"));
+
+ FrontendIptvSettingsFec frontendIptvSettingsFec {
+ .type = fecType,
+ .fecColNum = fecColNum,
+ .fecRowNum = fecRowNum,
+ };
+
+ return frontendIptvSettingsFec;
+}
+
+static FrontendSettings getIptvFrontendSettings(JNIEnv *env, const jobject &settings) {
+ FrontendSettings frontendSettings;
+ const char *clazzName = "android/media/tv/tuner/frontend/IptvFrontendSettings";
+ jclass clazz = env->FindClass(clazzName);
+ FrontendIptvSettingsProtocol protocol =
+ static_cast<FrontendIptvSettingsProtocol>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mProtocol", "I")));
+ FrontendIptvSettingsIgmp igmp =
+ static_cast<FrontendIptvSettingsIgmp>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mIgmp", "I")));
+ FrontendIptvSettingsFec fec = getIptvFrontendSettingsFec(env, settings);
+ int64_t bitrate = env->GetIntField(settings, env->GetFieldID(clazz, "mBitrate", "J"));
+ jstring contentUrlJString = (jstring) env->GetObjectField(settings, env->GetFieldID(
+ clazz, "mContentUrl",
+ "[Landroid/media/tv/tuner/frontend/IptvFrontendSettings;"));
+ const char *contentUrl = env->GetStringUTFChars(contentUrlJString, 0);
+ DemuxIpAddress ipAddr = getDemuxIpAddress(env, settings, clazzName);
+
+ FrontendIptvSettings frontendIptvSettings{
+ .protocol = protocol,
+ .fec = fec,
+ .igmp = igmp,
+ .bitrate = bitrate,
+ .ipAddr = ipAddr,
+ .contentUrl = contentUrl,
+ };
+ frontendSettings.set<FrontendSettings::Tag::iptv>(frontendIptvSettings);
+ return frontendSettings;
+}
+
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
ALOGV("getFrontendSettings %d", type);
FrontendType feType = static_cast<FrontendType>(type);
@@ -3408,6 +3564,8 @@
return getIsdbtFrontendSettings(env, settings);
case FrontendType::DTMB:
return getDtmbFrontendSettings(env, settings);
+ case FrontendType::IPTV:
+ return getIptvFrontendSettings(env, settings);
default:
// should never happen because a type is associated with a subclass of
// FrontendSettings and not set by users
@@ -3897,56 +4055,6 @@
return filterDownloadSettings;
}
-static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) {
- jclass clazz = env->FindClass("android/media/tv/tuner/filter/IpFilterConfiguration");
-
- jbyteArray jsrcIpAddress = static_cast<jbyteArray>(
- env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B")));
- jsize srcSize = env->GetArrayLength(jsrcIpAddress);
- jbyteArray jdstIpAddress = static_cast<jbyteArray>(
- env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B")));
- jsize dstSize = env->GetArrayLength(jdstIpAddress);
-
- DemuxIpAddress res;
-
- if (srcSize != dstSize) {
- // should never happen. Validated on Java size.
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize);
- return res;
- }
-
- if (srcSize == IP_V4_LENGTH) {
- vector<uint8_t> srcAddr;
- vector<uint8_t> dstAddr;
- srcAddr.resize(IP_V4_LENGTH);
- dstAddr.resize(IP_V4_LENGTH);
- env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
- env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
- res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(srcAddr);
- res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(dstAddr);
- } else if (srcSize == IP_V6_LENGTH) {
- vector<uint8_t> srcAddr;
- vector<uint8_t> dstAddr;
- srcAddr.resize(IP_V6_LENGTH);
- dstAddr.resize(IP_V6_LENGTH);
- env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
- env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
- res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(srcAddr);
- res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(dstAddr);
- } else {
- // should never happen. Validated on Java size.
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "Invalid IP address length %d", srcSize);
- return res;
- }
-
- res.srcPort = env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I"));
- res.dstPort = env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I"));
-
- return res;
-}
-
static DemuxFilterSettings getFilterConfiguration(
JNIEnv *env, int type, int subtype, jobject filterConfigObj) {
DemuxFilterSettings filterSettings;
@@ -4042,7 +4150,8 @@
break;
}
case DemuxFilterMainType::IP: {
- DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj);
+ DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj,
+ "android/media/tv/tuner/filter/IpFilterConfiguration");
DemuxIpFilterSettings ipFilterSettings {
.ipAddr = ipAddr,
};
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index dfbd7b5..27666caa 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -339,7 +339,7 @@
return reinterpret_cast<APerformanceHintSession*>(session)->sendHint(hint);
}
-int APerformanceHint_setThreads(APerformanceHintSession* session, const int32_t* threadIds,
+int APerformanceHint_setThreads(APerformanceHintSession* session, const pid_t* threadIds,
size_t size) {
if (session == nullptr) {
return EINVAL;
diff --git a/packages/CarrierDefaultApp/res/values-af/strings.xml b/packages/CarrierDefaultApp/res/values-af/strings.xml
index 0e77826..13ae4da 100644
--- a/packages/CarrierDefaultApp/res/values-af/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-af/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Gaan in elk geval deur blaaier voort"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Prestasiehupstoot"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Verbeter jou 5G-ervaring"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s beveel aan dat jy ’n werkverrigtinghupstootpakket koop. Tik om deur %2$s te koop."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Nie nou nie"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Bestuur"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Koop ’n prestasiehupstoot."</string>
diff --git a/packages/CarrierDefaultApp/res/values-am/strings.xml b/packages/CarrierDefaultApp/res/values-am/strings.xml
index 8ab8fbbb..e1f91ce 100644
--- a/packages/CarrierDefaultApp/res/values-am/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-am/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"የአፈጻጸም ጭማሪ"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"የእርስዎን የ5ጂ ተሞክሮ ያሻሽሉ"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s የአፈጻጸም መጨመሪያ ዕቅድ መግዛትን ይመክራል። በ%2$s ለመግዛት መታ ያድርጉ።"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"አሁን አይደለም"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"አስተዳድር"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"የአፈጻጸም ጭማሪ ይግዙ።"</string>
diff --git a/packages/CarrierDefaultApp/res/values-az/strings.xml b/packages/CarrierDefaultApp/res/values-az/strings.xml
index d8ee5b96..f64fcb6 100644
--- a/packages/CarrierDefaultApp/res/values-az/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-az/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Məsələn, giriş səhifəsi göstərilən təşkilata aid olmaya bilər."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Hər bir halda brazuer ilə davam edin"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performans artırması"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G təcrübənizi təkmilləşdirin"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s performans artırma planı almağı tövsiyə edir. %2$s ilə almaq üçün toxunun."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"İndi yox"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"İdarə edin"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Performans artırması alın."</string>
diff --git a/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
index f4adf53..5533bfd 100644
--- a/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi preko pregledača"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Poboljšanje učinka"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Poboljšajte 5G doživljaj"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s preporučuje kupovinu paketa za poboljšanje performansi. Dodirnite da biste kupili preko %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne sada"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljaj"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite poboljšanje učinka."</string>
diff --git a/packages/CarrierDefaultApp/res/values-be/strings.xml b/packages/CarrierDefaultApp/res/values-be/strings.xml
index 952a497..0053cda 100644
--- a/packages/CarrierDefaultApp/res/values-be/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-be/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Напрыклад, старонка ўваходу можа не належаць указанай арганізацыі."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Усё роўна працягнуць праз браўзер"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Павышэнне прадукцыйнасці"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Пашырце магчымасці 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s рэкамендуе купіць план павышэння прадукцыйнасці. Націсніце, каб купіць праз %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не цяпер"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Кіраваць"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Аплаціце павышэнне прадукцыйнасці."</string>
diff --git a/packages/CarrierDefaultApp/res/values-bs/strings.xml b/packages/CarrierDefaultApp/res/values-bs/strings.xml
index 052713b..7be8e2b 100644
--- a/packages/CarrierDefaultApp/res/values-bs/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-bs/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Naprimjer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi preko preglednika"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Pojačavanje performansi"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Poboljšajte iskustvo s 5G mrežom"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s preporučuje kupovinu paketa za poboljšanje performansi. Dodirnite da kupite koristeći %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne sada"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljajte"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite pojačavanje performansi."</string>
diff --git a/packages/CarrierDefaultApp/res/values-cs/strings.xml b/packages/CarrierDefaultApp/res/values-cs/strings.xml
index d638272..8a09421 100644
--- a/packages/CarrierDefaultApp/res/values-cs/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-cs/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Přihlašovací stránka například nemusí patřit zobrazované organizaci."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Přesto pokračovat prostřednictvím prohlížeče"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Zvýšení výkonu"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Získejte rychlejší připojení 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s doporučuje zakoupit zvýšení výkonu. Klepnutím ho zakoupíte přes operátora %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Teď ne"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Spravovat"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupte si zvýšení výkonu."</string>
diff --git a/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml b/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml
index de3baad..720dbc7 100644
--- a/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-en-rAU/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string>
diff --git a/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml b/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml
index 07340fb..87978ac 100644
--- a/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-en-rCA/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page may not belong to the organization shown."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string>
diff --git a/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml b/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml
index de3baad..720dbc7 100644
--- a/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-en-rGB/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string>
diff --git a/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml b/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml
index de3baad..720dbc7 100644
--- a/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-en-rIN/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page might not belong to the organisation shown."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string>
diff --git a/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml b/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml
index f42fdb7..7324a5b 100644
--- a/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-en-rXC/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"For example, the login page may not belong to the organization shown."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continue anyway via browser"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Performance boost"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Improve your 5G experience"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recommends buying a performance boost plan. Tap to buy through %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Not now"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Manage"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Purchase a performance boost."</string>
diff --git a/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml b/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml
index 23836ef..fedf1ac 100644
--- a/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continuar de todos modos desde el navegador"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento de rendimiento"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Mejora tu experiencia de 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomienda que compres un plan de aumento de rendimiento. Presiona para comprar mediante %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ahora no"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Administrar"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Compra un aumento de rendimiento."</string>
diff --git a/packages/CarrierDefaultApp/res/values-gu/strings.xml b/packages/CarrierDefaultApp/res/values-gu/strings.xml
index da4f94f..af09d13 100644
--- a/packages/CarrierDefaultApp/res/values-gu/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-gu/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ઉદાહરણ તરીકે, લોગિન પૃષ્ઠ બતાવવામાં આવેલી સંસ્થાનું ન પણ હોય."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"તો પણ બ્રાઉઝર મારફતે ચાલુ રાખો"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"પર્ફોર્મન્સ બૂસ્ટ"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"તમારા 5G અનુભવને બહેતર બનાવો"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s પર્ફોર્મન્સ બૂસ્ટ પ્લાન ખરીદવાનો સુઝાવ આપે છે. %2$s મારફતે ખરીદવા માટે ટૅપ કરો."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"હમણાં નહીં"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"મેનેજ કરો"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"પર્ફોર્મન્સ બૂસ્ટ ખરીદો."</string>
diff --git a/packages/CarrierDefaultApp/res/values-hi/strings.xml b/packages/CarrierDefaultApp/res/values-hi/strings.xml
index 6b12df7..e51b1a9 100644
--- a/packages/CarrierDefaultApp/res/values-hi/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-hi/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"उदाहरण के लिए, हो सकता है कि लॉगिन पेज दिखाए गए संगठन का ना हो."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ब्राउज़र के ज़रिए किसी भी तरह जारी रखें"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"परफ़ॉर्मेंस बूस्ट"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G का बेहतर अनुभव पाएं"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s, परफ़ॉर्मेंस को बेहतर बनाने वाले प्लान को खरीदने का सुझाव देता है. %2$s से प्लान खरीदने के लिए टैप करें."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"अभी नहीं"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"मैनेज करें"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"कोई परफ़ॉर्मेंस बूस्ट खरीदें."</string>
diff --git a/packages/CarrierDefaultApp/res/values-hr/strings.xml b/packages/CarrierDefaultApp/res/values-hr/strings.xml
index 96d8c3c..5a22ad5 100644
--- a/packages/CarrierDefaultApp/res/values-hr/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-hr/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi putem preglednika"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Poboljšanje izvedbe"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Poboljšajte svoj 5G doživljaj"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s preporučuje kupnju paketa za poboljšanje izvedbe. Dodirnite da biste kupili putem usluge %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne sad"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljajte"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite poboljšanje izvedbe."</string>
diff --git a/packages/CarrierDefaultApp/res/values-hy/strings.xml b/packages/CarrierDefaultApp/res/values-hy/strings.xml
index 63bb75c..49fbece 100644
--- a/packages/CarrierDefaultApp/res/values-hy/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-hy/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Օրինակ՝ մուտքի էջը կարող է ցուցադրված կազմակերպության էջը չլինել:"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Շարունակել դիտարկիչի միջոցով"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Արտադրողականության բարձրացում"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Բարելավեք 5G-ի օգտագործման ձեր փորձառությունը"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s օպերատորը խորհուրդ է տալիս ձեռք բերել արդյունավետությունը բարձրացնող սակագնային պլան։ Հպեք՝ %2$s-ի միջոցով գնելու համար։"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ոչ հիմա"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Կառավարել"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Բարձրացրեք ցանցի արտադրողականությունը վճարի դիմաց։"</string>
diff --git a/packages/CarrierDefaultApp/res/values-iw/strings.xml b/packages/CarrierDefaultApp/res/values-iw/strings.xml
index 8dc073c..c7229ba 100644
--- a/packages/CarrierDefaultApp/res/values-iw/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-iw/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"המשך בכל זאת באמצעות דפדפן"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"שיפור ביצועים"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"שיפור חווית השימוש ב-5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"יש המלצה של %1$s לקנות תוכנית לשיפור הביצועים. אפשר להקיש כדי לקנות דרך %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"לא עכשיו"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ניהול"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"רכישת שיפור ביצועים."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ja/strings.xml b/packages/CarrierDefaultApp/res/values-ja/strings.xml
index 01aa0d7..2fd86c8 100644
--- a/packages/CarrierDefaultApp/res/values-ja/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ja/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"たとえば、ログインページが表示されている組織に属していない可能性があります。"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ブラウザから続行"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"パフォーマンス ブースト"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G のエクスペリエンスを改善"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s さんがパフォーマンス ブースト プランの購入をおすすめしています。%2$sまでにタップして購入しましょう。"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"後で"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"管理"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"パフォーマンス ブーストを購入してください。"</string>
diff --git a/packages/CarrierDefaultApp/res/values-ka/strings.xml b/packages/CarrierDefaultApp/res/values-ka/strings.xml
index 6640254..76f7273 100644
--- a/packages/CarrierDefaultApp/res/values-ka/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ka/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"მაგალითად, სისტემაში შესვლის გვერდი შეიძლება არ ეკუთვნოდეს ნაჩვენებ ორგანიზაციას."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"მაინც ბრაუზერში გაგრძელება"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"ეფექტურობის გაძლიერება"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"გააუმჯობესეთ თქვენი 5G გამოცდილება"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s-ის მიერ რეკომენდებულია ეფექტურობის გაძლიერების გეგმის შეძენა. შეეხეთ %2$s-ის დახმარებით შესაძენად."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ახლა არა"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"მართვა"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"ეფექტურობის გაძლიერების შეძენა."</string>
diff --git a/packages/CarrierDefaultApp/res/values-kn/strings.xml b/packages/CarrierDefaultApp/res/values-kn/strings.xml
index b3ce678..c97d6f0 100644
--- a/packages/CarrierDefaultApp/res/values-kn/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-kn/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ಉದಾಹರಣೆಗೆ, ಲಾಗಿನ್ ಪುಟವು ತೋರಿಸಲಾಗಿರುವ ಸಂಸ್ಥೆಗೆ ಸಂಬಂಧಿಸಿಲ್ಲದಿರಬಹುದು."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ಪರವಾಗಿಲ್ಲ, ಬ್ರೌಸರ್ ಮೂಲಕ ಮುಂದುವರಿಸಿ"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"ಕಾರ್ಯಕ್ಷಮತೆ ಬೂಸ್ಟ್"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"ನಿಮ್ಮ 5G ಅನುಭವವನ್ನು ಸುಧಾರಿಸಿ"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಹೆಚ್ಚಿಸುವ ಪ್ಲಾನ್ ಅನ್ನು ಖರೀದಿಸಲು %1$s ಶಿಫಾರಸು ಮಾಡುತ್ತದೆ. %2$s ಮೂಲಕ ಖರೀದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ಈಗ ಬೇಡ"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ನಿರ್ವಹಿಸಿ"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"ಕಾರ್ಯಕ್ಷಮತೆ ಬೂಸ್ಟ್ ಅನ್ನು ಖರೀದಿಸಿ."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ko/strings.xml b/packages/CarrierDefaultApp/res/values-ko/strings.xml
index 3a59d1a..395627d 100644
--- a/packages/CarrierDefaultApp/res/values-ko/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ko/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"브라우저를 통해 계속하기"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"성능 향상"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G 사용 환경 개선"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s에서 성능 향상 계획 구매를 추천합니다. %2$s을(를) 통해 구매하려면 탭하세요."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"나중에"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"관리"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"성능 향상 구매"</string>
diff --git a/packages/CarrierDefaultApp/res/values-ky/strings.xml b/packages/CarrierDefaultApp/res/values-ky/strings.xml
index 368e706..b3970e3 100644
--- a/packages/CarrierDefaultApp/res/values-ky/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ky/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Мисалы, аккаунтка кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Баары бир серепчи аркылуу улантуу"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Иштин майнаптуулугун жогорулатуу"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G менен оңой иштеңиз"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s майнаптуулугун жогорулата турган тарифтик планды сатып алууну сунуштайт. %2$s аркылуу сатып алуу үчүн таптаңыз."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Азыр эмес"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Тескөө"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Иштин майнаптуулугун жогорулатууну сатып алыңыз."</string>
diff --git a/packages/CarrierDefaultApp/res/values-lo/strings.xml b/packages/CarrierDefaultApp/res/values-lo/strings.xml
index 722a8c5..70d8888 100644
--- a/packages/CarrierDefaultApp/res/values-lo/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-lo/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ຕົວຢ່າງ, ໜ້າເຂົ້າສູ່ລະບົບອາດຈະບໍ່ແມ່ນຂອງອົງກອນທີ່ປາກົດ."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ດຳເນີນການຕໍ່ຜ່ານໂປຣແກຣມທ່ອງເວັບ"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"ເລັ່ງປະສິດທິພາບ"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"ປັບປຸງປະສົບການ 5G ຂອງທ່ານ"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ແນະນຳໃຫ້ຊື້ແຜນການເລັ່ງປະສິດທິພາບ. ແຕະເພື່ອຊື້ຜ່ານ %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ບໍ່ຟ້າວເທື່ອ"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"ຈັດການ"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"ຊື້ການເລັ່ງປະສິດທິພາບ."</string>
diff --git a/packages/CarrierDefaultApp/res/values-lt/strings.xml b/packages/CarrierDefaultApp/res/values-lt/strings.xml
index e8817ef..8068416 100644
--- a/packages/CarrierDefaultApp/res/values-lt/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-lt/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Vis tiek tęsti naudojant naršyklę"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Našumo pagerinimas"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Pagerinkite 5G ryšį"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"„%1$s“ rekomenduoja įsigyti našumo pagerinimo planą. Palieskite, kad įsigytumėte naudodamiesi „%2$s“ paslaugomis."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne dabar"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Tvarkyti"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Įsigykite našumo pagerinimo paslaugą."</string>
diff --git a/packages/CarrierDefaultApp/res/values-mk/strings.xml b/packages/CarrierDefaultApp/res/values-mk/strings.xml
index a110920..425edfc 100644
--- a/packages/CarrierDefaultApp/res/values-mk/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-mk/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"На пример, страницата за најавување може да не припаѓа на прикажаната организација."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Сепак продолжи преку прелистувач"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Засилување на изведбата"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Подобрете го вашето доживување со 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s препорачува да купите пакет за засилување на изведбата. Допрете за да купите преку %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не сега"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Управувајте"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Купете засилување на изведбата."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ml/strings.xml b/packages/CarrierDefaultApp/res/values-ml/strings.xml
index 0757149..f258411 100644
--- a/packages/CarrierDefaultApp/res/values-ml/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ml/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ഉദാഹരണത്തിന്, കാണിച്ചിരിക്കുന്ന ഓർഗനൈസേഷന്റേതായിരിക്കില്ല ലോഗിൻ പേജ്."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"എന്തായാലും ബ്രൗസർ വഴി തുടരുക"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"പ്രകടന ബൂസ്റ്റ്"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"നിങ്ങളുടെ 5G അനുഭവം മെച്ചപ്പെടുത്തുക"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"പ്രകടന ബൂസ്റ്റ് പ്ലാൻ വാങ്ങാൻ %1$s നിർദ്ദേശിക്കുന്നു. %2$s വഴി വാങ്ങാൻ ടാപ്പ് ചെയ്യുക."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ഇപ്പോൾ വേണ്ട"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"മാനേജ് ചെയ്യുക"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"പ്രകടന ബൂസ്റ്റ് വാങ്ങൂ."</string>
diff --git a/packages/CarrierDefaultApp/res/values-mn/strings.xml b/packages/CarrierDefaultApp/res/values-mn/strings.xml
index da7f90c..12e1719 100644
--- a/packages/CarrierDefaultApp/res/values-mn/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-mn/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Жишээлбэл нэвтрэх хуудас нь харагдаж буй байгууллагынх биш байж болно."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Ямар ч тохиолдолд хөтчөөр үргэлжлүүлэх"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Гүйцэтгэлийн идэвхжүүлэлт"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G-н хэрэглээгээ сайжруулах"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s гүйцэтгэл нэмэгдүүлэх багцыг худалдаж авахыг зөвлөж байна. %2$s-р дамжуулан худалдан авах бол товшино уу."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Одоо биш"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Удирдах"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Гүйцэтгэлийн идэвхжүүлэлтийг худалдаж аваарай."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ms/strings.xml b/packages/CarrierDefaultApp/res/values-ms/strings.xml
index 6134874..85651f4 100644
--- a/packages/CarrierDefaultApp/res/values-ms/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ms/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Teruskan juga melalui penyemak imbas"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Peningkatan prestasi"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Tingkatkan pengalaman 5G anda"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s mengesyorkan pembelian pelan peningkatan prestasi. Ketik untuk membeli melalui %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Bukan sekarang"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Urus"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Beli perangsang prestasi."</string>
diff --git a/packages/CarrierDefaultApp/res/values-my/strings.xml b/packages/CarrierDefaultApp/res/values-my/strings.xml
index 1386b292..34c54b9 100644
--- a/packages/CarrierDefaultApp/res/values-my/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-my/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ဥပမာ− ဝင်ရောက်ရန် စာမျက်နှာသည် ပြသထားသည့် အဖွဲ့အစည်းနှင့် သက်ဆိုင်မှုမရှိခြင်း ဖြစ်နိုင်ပါသည်။"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"မည်သို့ပင်ဖြစ်စေ ဘရောက်ဇာမှတစ်ဆင့် ရှေ့ဆက်ရန်"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"စွမ်းဆောင်ရည် မြှင့်တင်အက်ပ်"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G အသုံးပြုမှု ပိုမိုကောင်းမွန်စေခြင်း"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s သည် စွမ်းဆောင်ရည်မြှင့်တင်သော အစီအစဉ်ဝယ်ရန် အကြံပြုပါသည်။ %2$s မှတစ်ဆင့် ဝယ်ရန် တို့ပါ။"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ယခုမလုပ်ပါ"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"စီမံရန်"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"စွမ်းဆောင်ရည် မြှင့်တင်အက်ပ် ဝယ်ယူရန်။"</string>
diff --git a/packages/CarrierDefaultApp/res/values-nb/strings.xml b/packages/CarrierDefaultApp/res/values-nb/strings.xml
index a283f01..b30e3d9 100644
--- a/packages/CarrierDefaultApp/res/values-nb/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-nb/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Det er for eksempel mulig at påloggingssiden ikke tilhører organisasjonen som vises."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Fortsett likevel via nettleseren"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Bedre ytelse"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Få en bedre 5G-opplevelse"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s anbefaler at du kjøper et abonnement for bedre ytelse. Trykk for å kjøpe via %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ikke nå"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Administrer"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Kjøp bedre ytelse."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ne/strings.xml b/packages/CarrierDefaultApp/res/values-ne/strings.xml
index 527a4ce..4dccdb9 100644
--- a/packages/CarrierDefaultApp/res/values-ne/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ne/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"उदाहरणका लागि, लग इन पृष्ठ देखाइएको संस्थाको नहुन सक्छ।"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"जे भए पनि ब्राउजर मार्फत जारी राख्नुहोस्"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"पर्फर्मेन्स बुस्ट"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G प्रयोग गर्दा अझ राम्रो सुविधा पाउनुहोस्"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ले पर्फर्मेन्स बुस्ट योजना खरिद गर्न सिफारिस गर्छ। %2$s मार्फत खरिद गर्न ट्याप गर्नुहोस्।"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"अहिले होइन"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"व्यवस्थापन गर्नुहोस्"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"पर्फर्मेन्स बुस्ट किन्नुहोस्।"</string>
diff --git a/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml b/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml
index a03c7c2..4a144be 100644
--- a/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continuar mesmo assim pelo navegador"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento de performance"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Melhore sua experiência 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomenda a compra de um plano para aumento de desempenho. Toque para comprar em %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Agora não"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gerenciar"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Comprar um aumento de performance."</string>
diff --git a/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml b/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml
index 08148f1..56e0c2d 100644
--- a/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continuar mesmo assim através do navegador"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento do desempenho"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Melhore a sua experiência 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomenda comprar um plano de melhoria do desempenho. Toque para comprar através do %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Agora não"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gerir"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Compre um aumento do desempenho."</string>
diff --git a/packages/CarrierDefaultApp/res/values-pt/strings.xml b/packages/CarrierDefaultApp/res/values-pt/strings.xml
index a03c7c2..4a144be 100644
--- a/packages/CarrierDefaultApp/res/values-pt/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-pt/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continuar mesmo assim pelo navegador"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Aumento de performance"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Melhore sua experiência 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomenda a compra de um plano para aumento de desempenho. Toque para comprar em %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Agora não"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gerenciar"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Comprar um aumento de performance."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ro/strings.xml b/packages/CarrierDefaultApp/res/values-ro/strings.xml
index 6c59687..f861137 100644
--- a/packages/CarrierDefaultApp/res/values-ro/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ro/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Continuă oricum prin browser"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Boost de performanță"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Îmbunătățește-ți experiența cu tehnologia 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s recomandă cumpărarea unui plan pentru îmbunătățirea performanței. Atinge pentru a cumpăra de la %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Nu acum"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gestionează"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Achiziționează un boost de performanță."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ru/strings.xml b/packages/CarrierDefaultApp/res/values-ru/strings.xml
index a7cf01d..f0bff17 100644
--- a/packages/CarrierDefaultApp/res/values-ru/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ru/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Например, страница входа в аккаунт может быть фиктивной."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Продолжить в браузере"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Повышение производительности"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Сделайте работу с 5G удобнее"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"\"%1$s\" рекомендует купить тарифный план, повышающий производительность. Чтобы приобрести тариф у оператора \"%2$s\", коснитесь экрана."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не сейчас"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Настроить"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Повысьте производительность сети за плату."</string>
diff --git a/packages/CarrierDefaultApp/res/values-sl/strings.xml b/packages/CarrierDefaultApp/res/values-sl/strings.xml
index 8c72ac3..3c1fd3e 100644
--- a/packages/CarrierDefaultApp/res/values-sl/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-sl/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Stran za prijavo na primer morda ne pripada prikazani organizaciji."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Vseeno nadaljuj v brskalniku"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Ojačevalnik zmogljivosti"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Izboljšajte izkušnjo omrežja 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s priporoča nakup paketa ojačevalnika zmogljivosti. Dotaknite se za nakup prek »%2$s«."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne zdaj"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljanje"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite ojačevalnik zmogljivosti."</string>
diff --git a/packages/CarrierDefaultApp/res/values-sr/strings.xml b/packages/CarrierDefaultApp/res/values-sr/strings.xml
index 990b531..d28bacc 100644
--- a/packages/CarrierDefaultApp/res/values-sr/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-sr/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"На пример, страница за пријављивање можда не припада приказаној организацији."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Ипак настави преко прегледача"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Побољшање учинка"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Побољшајте 5G доживљај"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s препоручује куповину пакета за побољшање перформанси. Додирните да бисте купили преко %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Не сада"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Управљај"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Купите побољшање учинка."</string>
diff --git a/packages/CarrierDefaultApp/res/values-te/strings.xml b/packages/CarrierDefaultApp/res/values-te/strings.xml
index d1c3086..f31291e 100644
--- a/packages/CarrierDefaultApp/res/values-te/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-te/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ఉదాహరణకు, లాగిన్ పేజీ చూపిన సంస్థకు చెందినది కాకపోవచ్చు."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించండి"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"పనితీరు బూస్ట్"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"మీ 5G అనుభవాన్ని మెరుగుపరుచుకోండి"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"పనితీరును బూస్ట్ చేసే ప్లాన్ను కొనుగోలు చేయమని %1$s సిఫార్సు చేస్తున్నారు. %2$s ద్వారా కొనుగోలు చేయడానికి ట్యాప్ చేయండి."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ఇప్పుడు కాదు"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"మేనేజ్ చేయండి"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"పనితీరు బూస్ట్ను కొనుగోలు చేయండి."</string>
diff --git a/packages/CarrierDefaultApp/res/values-th/strings.xml b/packages/CarrierDefaultApp/res/values-th/strings.xml
index 1b5749c..f20346e 100644
--- a/packages/CarrierDefaultApp/res/values-th/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-th/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"ดำเนินการต่อผ่านเบราว์เซอร์"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"การเพิ่มประสิทธิภาพ"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"ปรับปรุงประสบการณ์การใช้งาน 5G ของคุณ"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s แนะนำให้ซื้อแพ็กเกจเพิ่มประสิทธิภาพ แตะเพื่อซื้อผ่าน %2$s"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ไว้ทีหลัง"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"จัดการ"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"ซื้อการเพิ่มประสิทธิภาพ"</string>
diff --git a/packages/CarrierDefaultApp/res/values-tl/strings.xml b/packages/CarrierDefaultApp/res/values-tl/strings.xml
index 6543c68..9e8029a 100644
--- a/packages/CarrierDefaultApp/res/values-tl/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-tl/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Halimbawa, maaaring hindi pag-aari ng ipinapakitang organisasyon ang page ng login."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Magpatuloy pa rin sa pamamagitan ng browser"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Pag-boost ng performance"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Pagandahin ang iyong karanasan sa 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"Inirerekomenda ng %1$s na bumili ng plan sa performance boost. I-tap para bumili sa pamamagitan ng %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Huwag muna"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Pamahalaan"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Bumili ng pang-boost ng performance."</string>
diff --git a/packages/CarrierDefaultApp/res/values-ur/strings.xml b/packages/CarrierDefaultApp/res/values-ur/strings.xml
index e878156..681998b 100644
--- a/packages/CarrierDefaultApp/res/values-ur/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ur/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"مثال کے طور پر ہو سکتا ہے کہ لاگ ان صفحہ دکھائی گئی تنظیم سے تعلق نہ رکھتا ہو۔"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"براؤزر کے ذریعے بہرحال جاری رکھیں"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"پرفارمینس بوسٹ"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"اپنے 5G تجربے کو بہتر بنائیں"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s ایک پرفارمنس بوسٹ پلان کی تجویز کرتا ہے۔ %2$s استعمال کر کے خریدنے کے لیے تھپتھپائیں۔"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"ابھی نہیں"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"نظم کریں"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"پرفارمینس بوسٹ خریدیں۔"</string>
diff --git a/packages/CarrierDefaultApp/res/values-uz/strings.xml b/packages/CarrierDefaultApp/res/values-uz/strings.xml
index 9c77538..47006f6 100644
--- a/packages/CarrierDefaultApp/res/values-uz/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-uz/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Masalan, tizimga kirish sahifasi ko‘rsatilgan tashkilotga tegishli bo‘lmasligi mumkin."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Brauzerda davom ettirish"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Unumdorlikni kuchaytirish"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"5G bilan ishlashni qulaylashtiring"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s unumdorlikni oshiradigan tarif rejasini xarid qilishni tavsiya etadi. %2$s orqali xarid qilish uchun bosing."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Hozir emas"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Boshqarish"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Unumdorlikni kuchaytirish xizmatini xarid qiling."</string>
diff --git a/packages/CarrierDefaultApp/res/values-vi/strings.xml b/packages/CarrierDefaultApp/res/values-vi/strings.xml
index 16a740e..968b6e1 100644
--- a/packages/CarrierDefaultApp/res/values-vi/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-vi/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Ví dụ: trang đăng nhập có thể không thuộc về tổ chức được hiển thị."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Vẫn tiếp tục qua trình duyệt"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Tăng hiệu suất"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Cải thiện trải nghiệm sử dụng 5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"%1$s đề xuất bạn mua gói tăng cường hiệu suất. Nhấn để mua thông qua %2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Để sau"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Quản lý"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Mua gói tăng hiệu suất."</string>
diff --git a/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml b/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml
index d5ddc1a..53b71ab 100644
--- a/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-zh-rHK/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"例如,登入頁面可能並不屬於所顯示的機構。"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"仍要透過瀏覽器繼續操作"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"效能提升服務"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"改善 5G 體驗"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"「%1$s」建議購買效能提升方案,輕觸即可透過「%2$s」購買。"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"暫時不要"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"管理"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"購買效能提升服務。"</string>
diff --git a/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml b/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml
index d8a6262..332ab9c 100644
--- a/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-zh-rTW/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"仍要透過瀏覽器繼續操作"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"效能提升方案"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"改善 5G 體驗"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"「%1$s」建議購買效能提升方案,輕觸即可透過「%2$s」購買。"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"暫時不要"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"管理"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"購買效能提升方案。"</string>
diff --git a/packages/CarrierDefaultApp/res/values-zu/strings.xml b/packages/CarrierDefaultApp/res/values-zu/strings.xml
index 5edadd6..ae84695 100644
--- a/packages/CarrierDefaultApp/res/values-zu/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-zu/strings.xml
@@ -15,10 +15,8 @@
<string name="ssl_error_example" msgid="6188711843183058764">"Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Qhubeka noma kunjalo ngesiphequluli"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"I-boost yokusebenza"</string>
- <!-- no translation found for performance_boost_notification_title (946857427149305992) -->
- <skip />
- <!-- no translation found for performance_boost_notification_detail (362407668982648351) -->
- <skip />
+ <string name="performance_boost_notification_title" msgid="946857427149305992">"Thuthukisa umuzwa wakho we-5G"</string>
+ <string name="performance_boost_notification_detail" msgid="362407668982648351">"I-%1$s incoma ukuthenga uhlelo lokuthuthukisa ukusebenza. Thepha ukuze uthenge nge-%2$s."</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Hhayi manje"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Phatha"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Thenga i-boost yokusebenza."</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index 6ebe205..655b843 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -43,8 +43,7 @@
<string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
<string name="consent_back" msgid="2560683030046918882">"Til baka"</string>
<string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Veita forritum í <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> sömu heimildir og í <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string>
- <!-- no translation found for permission_sync_summary (765497944331294275) -->
- <skip />
+ <string name="permission_sync_summary" msgid="765497944331294275">"Þetta getur átt við um <strong>hljóðnema</strong>, <strong>myndavél</strong>, <strong>aðgang að staðsetningu</strong> og aðrar heimildir fyrir viðkvæmu efni í <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>. <br/><br/>Þú getur breytt þessum heimildum hvenær sem er í stillingunum í <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>."</string>
<string name="vendor_icon_description" msgid="4445875290032225965">"Tákn forrits"</string>
<string name="vendor_header_button_description" msgid="6566660389500630608">"Hnappur fyrir upplýsingar"</string>
<string name="permission_phone" msgid="2661081078692784919">"Sími"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index c9106c3..97db70a 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
<string name="confirmation_title" msgid="3785000297483688997">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot je <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="summary_watch" msgid="4085794790142204006">"De app is vereist om je <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="APP_NAME">%2$s</xliff:g> kan interactie hebben met je meldingen en toegang krijgen tot rechten voor Telefoon, Sms, Contacten, Agenda, Gesprekslijsten en Apparaten in de buurt."</string>
<string name="summary_watch_single_device" msgid="1523091550243476756">"De app is vereist om je <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="APP_NAME">%2$s</xliff:g> heeft toestemming om interactie te hebben met de volgende rechten:"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 7ab2992..6f19a84 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -57,13 +57,13 @@
<string name="permission_notification" msgid="693762568127741203">"通知"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"應用程式"</string>
<string name="permission_nearby_device_streaming" msgid="5868108148065023161">"附近的裝置串流"</string>
- <string name="permission_phone_summary" msgid="6684396967861278044">"可撥打及管理通話"</string>
- <string name="permission_call_logs_summary" msgid="6186103394658755022">"可讀取及寫入通話記錄"</string>
- <string name="permission_sms_summary" msgid="3508442683678912017">"可傳送及查看簡訊"</string>
- <string name="permission_contacts_summary" msgid="675861979475628708">"可存取聯絡人"</string>
+ <string name="permission_phone_summary" msgid="6684396967861278044">"可撥打電話和管理通話"</string>
+ <string name="permission_call_logs_summary" msgid="6186103394658755022">"可讀取及寫入手機通話記錄"</string>
+ <string name="permission_sms_summary" msgid="3508442683678912017">"可傳送及查看短訊"</string>
+ <string name="permission_contacts_summary" msgid="675861979475628708">"可存取通訊錄"</string>
<string name="permission_calendar_summary" msgid="6460000922511766226">"可存取日曆"</string>
<string name="permission_microphone_summary" msgid="4241354865859396558">"可使用麥克風錄音"</string>
- <string name="permission_nearby_devices_summary" msgid="931940524460876655">"可尋找、連線及判斷鄰近裝置的相對位置"</string>
+ <string name="permission_nearby_devices_summary" msgid="931940524460876655">"可尋找、連接及判斷附近裝置的相對位置"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"可以讀取所有通知,包括聯絡人、訊息和電話等資訊"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"串流播放手機應用程式內容"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml
index 6d225c3..723f21c 100644
--- a/packages/CredentialManager/res/values-af/strings.xml
+++ b/packages/CredentialManager/res/values-af/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Kanselleer"</string>
<string name="string_continue" msgid="1346732695941131882">"Gaan voort"</string>
<string name="string_more_options" msgid="7990658711962795124">"Meer opsies"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Veiliger met wagwoordsleutels"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Met wagwoordsleutels hoef jy nie komplekse wagwoorde te skep of te onthou nie"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Wagwoordsleutels is geënkripteerde digitale sleutels wat jy met jou vingerafdruk, gesig of skermslot skep"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Hulle word in ’n wagwoordbestuurder gestoor sodat jy op ander toestelle kan aanmeld"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"Kies waar om jou <xliff:g id="CREATETYPES">%1$s</xliff:g> te stoor"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Kies ’n wagwoordbestuurder om jou inligting te stoor en volgende keer vinniger aan te meld"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Skep wagwoordsleutel vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml
index 8c52cf3..3d482f9 100644
--- a/packages/CredentialManager/res/values-am/strings.xml
+++ b/packages/CredentialManager/res/values-am/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"ይቅር"</string>
<string name="string_continue" msgid="1346732695941131882">"ቀጥል"</string>
<string name="string_more_options" msgid="7990658711962795124">"ተጨማሪ አማራጮች"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"በይለፍ ቃል ይበልጥ ደህንነቱ የተጠበቀ"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"በይለፍ ቁልፎች ውስብስብ የይለፍ ቁልፎችን መፍጠር ወይም ማስታወስ አያስፈልግዎትም"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"የይለፍ ቁልፎች የእርስዎን የጣት አሻራ፣ መልክ ወይም የማያ ገጽ መቆለፊያ በመጠቀም የሚፈጥሯቸው የተመሰጠሩ ዲጂታል ቆልፎች ናቸው"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"በሌሎች መሣሪያዎች ላይ መግባት እንዲችሉ በሚስጥር ቁልፍ አስተዳዳሪ ላይ ይቀመጣሉ"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"የእርስዎን <xliff:g id="CREATETYPES">%1$s</xliff:g> የት እንደሚያስቀምጡ ይምረጡ"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"መረጃዎን ለማስቀመጥ እና በቀጣይ ጊዜ በፍጥነት በመለያ ለመግባት የሚስጥር ቁልፍ አስተዳዳሪን ይምረጡ"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ለ<xliff:g id="APPNAME">%1$s</xliff:g> የይለፍ ቁልፍ ይፈጠር?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"ለ<xliff:g id="APPNAME">%1$s</xliff:g> የይለፍ ቃል ይቀመጥ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"ለ<xliff:g id="APPNAME">%1$s</xliff:g> የመግቢያ መረጃ ይቀመጥ?"</string>
<string name="passkey" msgid="632353688396759522">"የይለፍ ቁልፍ"</string>
<string name="password" msgid="6738570945182936667">"የይለፍ ቃል"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"የይለፍ ቁልፎች"</string>
+ <string name="passwords" msgid="5419394230391253816">"የይለፍ ቃላት"</string>
<string name="sign_ins" msgid="4710739369149469208">"መግቢያዎች"</string>
<string name="sign_in_info" msgid="2627704710674232328">"የመግቢያ መረጃ"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>ን አስቀምጥ ወደ"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"በሌላ መሣሪያ ውስጥ የይለፍ ቁልፍ ይፈጠር?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ለሁሉም መግቢያዎችዎ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ን ይጠቀሙ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"ይህ የሚስጥር ቁልፍ አስተዳዳሪ እርስዎን በቀላሉ በመለያ እንዲገቡ ለማገዝ የእርስዎን የይለፍ ቃላት እና የይለፍ ቁልፎችን ያከማቻል"</string>
<string name="set_as_default" msgid="4415328591568654603">"እንደ ነባሪ ያዋቅሩ"</string>
<string name="use_once" msgid="9027366575315399714">"አንዴ ይጠቀሙ"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> የይለፍ ቃሎች • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> የይለፍ ቁልፎች"</string>
diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml
index 6b90817..47e41a7 100644
--- a/packages/CredentialManager/res/values-ar/strings.xml
+++ b/packages/CredentialManager/res/values-ar/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"إلغاء"</string>
<string name="string_continue" msgid="1346732695941131882">"متابعة"</string>
<string name="string_more_options" msgid="7990658711962795124">"خيارات إضافية"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"توفير المزيد من الأمان باستخدام مفاتيح المرور"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"باستخدام مفاتيح المرور، لا حاجة لإنشاء كلمات مرور معقدة أو تذكّرها."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"مفاتيح المرور هي مفاتيح رقمية مشفّرة يمكنك إنشاؤها باستخدام بصمة الإصبع أو التعرّف على الوجه أو قفل الشاشة."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"يتم حفظها في مدير كلمات مرور، حتى تتمكن من تسجيل الدخول على أجهزة أخرى."</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"اختَر الموقع الذي تريد حفظ <xliff:g id="CREATETYPES">%1$s</xliff:g> فيه"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"اختَر مدير كلمات مرور لحفظ معلوماتك وتسجيل الدخول بشكل أسرع في المرة القادمة."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"إنشاء مفتاح مرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"هل تريد حفظ كلمة المرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"هل تريد حفظ معلومات تسجيل الدخول لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
<string name="passkey" msgid="632353688396759522">"مفتاح مرور"</string>
<string name="password" msgid="6738570945182936667">"كلمة المرور"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"مفاتيح المرور"</string>
+ <string name="passwords" msgid="5419394230391253816">"كلمات المرور"</string>
<string name="sign_ins" msgid="4710739369149469208">"عمليات تسجيل الدخول"</string>
<string name="sign_in_info" msgid="2627704710674232328">"معلومات تسجيل الدخول"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"حفظ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> في"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"هل تريد إنشاء مفتاح المرور في خدمة أخرى؟"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"هل تريد استخدام \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" لكل عمليات تسجيل الدخول؟"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"سيخزِّن مدير كلمات المرور هذا كلمات المرور ومفاتيح المرور لمساعدتك في تسجيل الدخول بسهولة."</string>
<string name="set_as_default" msgid="4415328591568654603">"ضبط الخيار كتلقائي"</string>
<string name="use_once" msgid="9027366575315399714">"الاستخدام مرة واحدة"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> كلمة مرور • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> مفتاح مرور"</string>
diff --git a/packages/CredentialManager/res/values-as/strings.xml b/packages/CredentialManager/res/values-as/strings.xml
index 3a92b68..2cea9c8 100644
--- a/packages/CredentialManager/res/values-as/strings.xml
+++ b/packages/CredentialManager/res/values-as/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"বাতিল কৰক"</string>
<string name="string_continue" msgid="1346732695941131882">"অব্যাহত ৰাখক"</string>
<string name="string_more_options" msgid="7990658711962795124">"অধিক বিকল্প"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"পাছকীৰ জৰিয়তে অধিক সুৰক্ষিত"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"পাছকী ব্যৱহাৰ কৰিলে আপুনি জটিল পাছৱৰ্ড সৃষ্টি কৰিব অথবা মনত ৰাখিব নালাগে"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"পাছকীসমূহ হৈছে আপুনি আপোনাৰ ফিংগাৰপ্ৰিণ্ট, মুখাৱয়ব অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰি সৃষ্টি কৰা এনক্ৰিপ্ট কৰা ডিজিটেল চাবি"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"সেইসমূহ এটা পাছৱৰ্ড পৰিচালকত ছেভ কৰা হয়, যাতে আপুনি অন্য ডিভাইচসমূহত ছাইন ইন কৰিব পাৰে"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"আপোনাৰ <xliff:g id="CREATETYPES">%1$s</xliff:g> ক’ত ছেভ কৰিব লাগে সেয়া বাছনি কৰক"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"আপোনাৰ তথ্য ছেভ কৰি পৰৱৰ্তী সময়ত দ্ৰুতভাৱে ছাইন ইন কৰিবলৈ এটা পাছৱৰ্ড পৰিচালক বাছনি কৰক"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>ৰ বাবে পাছকী সৃষ্টি কৰিবনে?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g>ৰ বাবে পাছৱৰ্ড ছেভ কৰিবনে?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g>ৰ বাবে ছাইন ইনৰ তথ্য ছেভ কৰিবনে?"</string>
<string name="passkey" msgid="632353688396759522">"পাছকী"</string>
<string name="password" msgid="6738570945182936667">"পাছৱৰ্ড"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"পাছকী"</string>
+ <string name="passwords" msgid="5419394230391253816">"পাছৱৰ্ড"</string>
<string name="sign_ins" msgid="4710739369149469208">"ছাইন-ইন"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ছাইন ইনৰ তথ্য"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ইয়াত ছেভ কৰক"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"অন্য এটা ডিভাইচত পাছকী সৃষ্টি কৰিবনে?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"আপোনাৰ আটাইবোৰ ছাইন ইনৰ বাবে <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবনে?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"আপোনাক সহজে ছাইন ইন কৰাত সহায় কৰিবলৈ এই পাছৱৰ্ড পৰিচালকটোৱে আপোনাৰ পাছৱৰ্ড আৰু পাছকী ষ্ট’ৰ কৰিব"</string>
<string name="set_as_default" msgid="4415328591568654603">"ডিফ’ল্ট হিচাপে ছেট কৰক"</string>
<string name="use_once" msgid="9027366575315399714">"এবাৰ ব্যৱহাৰ কৰক"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> টা পাছৱৰ্ড • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> টা পাছকী"</string>
diff --git a/packages/CredentialManager/res/values-az/strings.xml b/packages/CredentialManager/res/values-az/strings.xml
index d7f3025..a987b66 100644
--- a/packages/CredentialManager/res/values-az/strings.xml
+++ b/packages/CredentialManager/res/values-az/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Ləğv edin"</string>
<string name="string_continue" msgid="1346732695941131882">"Davam edin"</string>
<string name="string_more_options" msgid="7990658711962795124">"Digər seçimlər"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Giriş açarları ilə daha təhlükəsiz"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Giriş açarları ilə mürəkkəb parollar yaratmağa və ya yadda saxlamağa ehtiyac yoxdur"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Giriş açarları barmaq izi, üz və ya ekran kilidindən istifadə edərək yaratdığınız şifrələnmiş rəqəmsal açarlardır"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Onlar parol menecerində saxlanılır ki, digər cihazlarda daxil ola biləsiniz"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> elementinin saxlanacağı yeri seçin"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Məlumatlarınızı yadda saxlamaq və növbəti dəfə daha sürətli daxil olmaq üçün parol meneceri seçin"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> üçün giriş açarı yaradılsın?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> üçün parol yadda saxlanılsın?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> üçün giriş məlumatları yadda saxlansın?"</string>
<string name="passkey" msgid="632353688396759522">"giriş açarı"</string>
<string name="password" msgid="6738570945182936667">"parol"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"giriş açarları"</string>
+ <string name="passwords" msgid="5419394230391253816">"parollar"</string>
<string name="sign_ins" msgid="4710739369149469208">"girişlər"</string>
<string name="sign_in_info" msgid="2627704710674232328">"Giriş məlumatları"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> burada yadda saxlansın:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Başqa cihazda giriş açarı yaradılsın?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Bütün girişlər üçün <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> istifadə edilsin?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Bu parol meneceri asanlıqla daxil olmanıza kömək etmək üçün parollarınızı və giriş açarlarınızı saxlayacaq"</string>
<string name="set_as_default" msgid="4415328591568654603">"Defolt olaraq seçin"</string>
<string name="use_once" msgid="9027366575315399714">"Bir dəfə istifadə edin"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parol • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> giriş açarı"</string>
diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
index 6e8ec71..5a52190 100644
--- a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Otkaži"</string>
<string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
<string name="string_more_options" msgid="7990658711962795124">"Još opcija"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Bezbednije uz pristupne kodove"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Uz pristupne kodove nema potrebe da pravite ili pamtite složene lozinke"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Pristupni kodovi su šifrovani digitalni kodovi koje pravite pomoću otiska prsta, lica ili zaključavanja ekrana"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Čuvaju se u menadžeru lozinki da biste mogli da se prijavljujete na drugim uređajima"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Odaberite gde ćete sačuvati stavke <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Izaberite menadžera lozinki da biste sačuvali podatke i brže se prijavili sledeći put"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Želite da napravite pristupni kôd za: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Želite da sačuvate lozinku za: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Želite da sačuvate podatke za prijavljivanje za: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"pristupni kôd"</string>
<string name="password" msgid="6738570945182936667">"lozinka"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"pristupni kodovi"</string>
+ <string name="passwords" msgid="5419394230391253816">"lozinke"</string>
<string name="sign_ins" msgid="4710739369149469208">"prijavljivanja"</string>
<string name="sign_in_info" msgid="2627704710674232328">"podaci za prijavljivanje"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Sačuvajte stavku<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> u"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Želite da napravite pristupni kôd na drugom uređaju?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite da za sva prijavljivanja koristite: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Ovaj menadžer lozinki će čuvati lozinke i pristupne kodove da biste se lako prijavljivali"</string>
<string name="set_as_default" msgid="4415328591568654603">"Podesi kao podrazumevano"</string>
<string name="use_once" msgid="9027366575315399714">"Koristi jednom"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Pristupnih kodova:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml
index 99af511..6cad43d 100644
--- a/packages/CredentialManager/res/values-be/strings.xml
+++ b/packages/CredentialManager/res/values-be/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Скасаваць"</string>
<string name="string_continue" msgid="1346732695941131882">"Далей"</string>
<string name="string_more_options" msgid="7990658711962795124">"Дадатковыя параметры"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"З ключамі доступу вам будзе бяспечней."</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Дзякуючы ключам доступу вам не трэба ствараць і запамінаць складаныя паролі."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Ключы доступу – гэта зашыфраваныя лючбавыя ключы, створаныя вамі з дапамогай адбітка пальца, твару ці блакіроўкі экрана."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Яны захаваны ў менеджары пароляў. Дзякуючы гэтаму вы можаце ўваходзіць на іншых прыладах"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Выберыце, куды захаваць <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Выберыце менеджар пароляў, каб захаваць свае даныя і забяспечыць хуткі ўваход у наступныя разы"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Стварыце ключ доступу да праграмы \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Захаваць пароль для праграмы \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Захаваць інфармацыю пра спосаб уваходу ў праграму \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ключы доступу"</string>
+ <string name="passwords" msgid="5419394230391253816">"паролі"</string>
<string name="sign_ins" msgid="4710739369149469208">"спосабы ўваходу"</string>
<string name="sign_in_info" msgid="2627704710674232328">"інфармацыя пра спосабы ўваходу"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Захаваць <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> сюды:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Стварыць ключ доступу на іншай прыладзе?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Выкарыстоўваць папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" для ўсіх спосабаў уваходу?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Каб вам было прасцей уваходзіць у сістэму, вашы паролі і ключы доступу будуць захоўвацца ў менеджары пароляў"</string>
<string name="set_as_default" msgid="4415328591568654603">"Выкарыстоўваць стандартна"</string>
<string name="use_once" msgid="9027366575315399714">"Скарыстаць адзін раз"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Пароляў: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Ключоў доступу: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml
index 13ebe24..163398e 100644
--- a/packages/CredentialManager/res/values-bg/strings.xml
+++ b/packages/CredentialManager/res/values-bg/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Отказ"</string>
<string name="string_continue" msgid="1346732695941131882">"Напред"</string>
<string name="string_more_options" msgid="7990658711962795124">"Още опции"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"По-сигурно с помощта на кодове за достъп"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Когато използвате кодове за достъп, не е необходимо да създавате, нито да помните сложни пароли"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Кодовете за достъп са шифровани дигитални ключове, които създавате посредством отпечатъка, лицето си или опцията си за заключване на екрана"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Данните се запазват в мениджър на пароли, за да можете да влизате в профила си на други устройства"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Изберете къде да запазите своите <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Изберете мениджър на пароли, в който да се запазят данните ви, така че следващия път да влезете по-бързо в профила си"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Да се създаде ли код за достъп за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Да се запази ли паролата за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Да се запазят ли данните за вход за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"код за достъп"</string>
<string name="password" msgid="6738570945182936667">"парола"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"кодове за достъп"</string>
+ <string name="passwords" msgid="5419394230391253816">"пароли"</string>
<string name="sign_ins" msgid="4710739369149469208">"данни за вход"</string>
<string name="sign_in_info" msgid="2627704710674232328">"данните за вход"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Запазване на <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> във:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Искате ли да създадете код за достъп на друго устройство?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Да се използва ли <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> за всичките ви данни за вход?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Този мениджър на пароли ще съхранява вашите пароли и кодове за достъп, за да влизате лесно в профила си"</string>
<string name="set_as_default" msgid="4415328591568654603">"Задаване като основно"</string>
<string name="use_once" msgid="9027366575315399714">"Еднократно използване"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> пароли • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> кода за достъп"</string>
diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml
index a19fe71..408ccb1 100644
--- a/packages/CredentialManager/res/values-bn/strings.xml
+++ b/packages/CredentialManager/res/values-bn/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"বাতিল করুন"</string>
<string name="string_continue" msgid="1346732695941131882">"চালিয়ে যান"</string>
<string name="string_more_options" msgid="7990658711962795124">"আরও বিকল্প"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"\'পাসকী\'-এর সাথে সুরক্ষিত"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"\'পাসকী\' ব্যবহার করলে জটিল পাসওয়ার্ড তৈরি করার বা মনে রাখার কোনও প্রয়োজন নেই"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"আপনার ফিঙ্গারপ্রিন্ট, ফেস মডেল বা \'স্ক্রিন লক\' ব্যবহার করে আপনি যে এনক্রিপটেড ডিজিটাল \'কী\' তৈরি করেন সেগুলিই হল \'পাসকী\'"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"আপনি যাতে অন্যান্য ডিভাইসে সাইন-ইন করতে পারেন তার জন্য Password Manager-এ এগুলি সেভ করা হয়"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"আপনার <xliff:g id="CREATETYPES">%1$s</xliff:g> কোথায় সেভ করবেন তা বেছে নিন"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"আপনার তথ্য সেভ করতে একটি Password Manager বেছে নিন এবং পরের বার আরও দ্রুত সাইন-ইন করুন"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>-এর জন্য \'পাসকী\' তৈরি করবেন?"</string>
diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml
index 6b776f5..6d3a184 100644
--- a/packages/CredentialManager/res/values-bs/strings.xml
+++ b/packages/CredentialManager/res/values-bs/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Otkaži"</string>
<string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
<string name="string_more_options" msgid="7990658711962795124">"Više opcija"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Saznajte više"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Sigurniji ste uz pristupne ključeve"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Uz pristupne ključeve ne morate kreirati ili pamtiti složene lozinke"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Pristupni ključevi su šifrirani digitalni ključevi koje kreirate pomoću otiska prsta, lica ili zaključavanja ekrana"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Pristupni ključevi se pohranjuju u upravitelju lozinki da se možete prijaviti na drugim uređajima"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Više informacija o pristupnim ključevima"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Tehnologija bez upotrebe zaporke"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Pristupni ključevi omogućuju prijavu bez upotrebe zaporki. Treba vam samo otisak prsta, prepoznavanje lica, PIN ili uzorak pokreta prstom da biste potvrdili svoj identitet i izradili pristupni ključ."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Kriptografija javnog ključa"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Na temelju saveza FIDO (koji uključuje Google, Apple, Microsoft i mnoge druge) i standarda W3C pristupni ključevi koriste kriptografske ključeve. Za razliku od korisničkog imena i niza znakova za zaporke, privatno-javni ključ izrađen je za aplikaciju ili web-lokaciju. Privatni ključ pohranjen je na vašem uređaju ili upravitelju zaporki i potvrđuje vaš identitet. Javni se ključ dijeli s poslužiteljem aplikacije ili web-lokacije. Uz odgovarajuće ključeve možete se odmah registrirati i prijaviti."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Poboljšana sigurnost računa"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Svaki ključ povezan isključivo s aplikacijom ili web-lokacijom za koju je izrađen, stoga se nikad ne možete pogreškom prijaviti u prijevarnu aplikaciju ili na web-lokaciju. Osim toga, kad je riječ o poslužiteljima na kojem se nalaze samo javni ključevi, hakiranje je mnogo teže."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Besprijekorni prijelaz"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Kako idemo u smjeru budućnosti bez zaporki, one će i dalje biti dostupne uz pristupne ključeve."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Odaberite gdje sačuvati stavku <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Odaberite upravitelja lozinki da sačuvate svoje informacije i brže se prijavite sljedeći put"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Kreirati pristupni ključ za aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml
index 8943f46..5d59484 100644
--- a/packages/CredentialManager/res/values-ca/strings.xml
+++ b/packages/CredentialManager/res/values-ca/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancel·la"</string>
<string name="string_continue" msgid="1346732695941131882">"Continua"</string>
<string name="string_more_options" msgid="7990658711962795124">"Més opcions"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Més seguretat amb les claus d\'accés"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Amb les claus d\'accés, no cal que creïs ni recordis contrasenyes difícils"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Les claus d\'accés són claus digitals encriptades que pots crear amb la teva cara, l\'empremta digital o el bloqueig de pantalla"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Es desen a un gestor de contrasenyes perquè puguis iniciar la sessió en altres dispositius"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Tria on vols desar les <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un gestor de contrasenyes per desar la teva informació i iniciar la sessió més ràpidament la pròxima vegada"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vols crear la clau d\'accés per a <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Vols desar la contrasenya per a <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vols desar la informació d\'inici de sessió per a <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clau d\'accés"</string>
<string name="password" msgid="6738570945182936667">"contrasenya"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"claus d\'accés"</string>
+ <string name="passwords" msgid="5419394230391253816">"contrasenyes"</string>
<string name="sign_ins" msgid="4710739369149469208">"inicis de sessió"</string>
<string name="sign_in_info" msgid="2627704710674232328">"informació d\'inici de sessió"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Desa <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> a"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Vols crear una clau d\'accés en un altre dispositiu?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Vols utilitzar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> per a tots els teus inicis de sessió?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Aquest gestor de contrasenyes emmagatzemarà les teves contrasenyes i claus d\'accés per ajudar-te a iniciar la sessió fàcilment"</string>
<string name="set_as_default" msgid="4415328591568654603">"Estableix com a predeterminada"</string>
<string name="use_once" msgid="9027366575315399714">"Utilitza un cop"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasenyes • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> claus d\'accés"</string>
diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml
index 06f94d2..de5c5b6 100644
--- a/packages/CredentialManager/res/values-cs/strings.xml
+++ b/packages/CredentialManager/res/values-cs/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"Zrušit"</string>
<string name="string_continue" msgid="1346732695941131882">"Pokračovat"</string>
<string name="string_more_options" msgid="7990658711962795124">"Další možnosti"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Další informace"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Přístupové klíče zvyšují bezpečnost"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"S přístupovými klíči si nemusíte vytvářet ani pamatovat složitá hesla"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Přístupové klíče jsou šifrované digitální klíče, které vytvoříte pomocí otisku prstu, obličeje nebo zámku obrazovky"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Přístupové klíče se ukládají do správce hesel, takže se můžete přihlásit na jiných zařízeních"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Další informace o přístupových klíčích"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Technologie bez hesel"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Přístupové klíče umožňují přihlašovat se bez hesel. Stačí pomocí otisku prstu, rozpoznání obličeje, kódu PIN nebo gesta ověřit svou totožnost a vytvořit přístupový klíč."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Kryptografie s veřejným klíčem"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Podle pokynů FIDO Alliance (která zahrnuje společnosti Google, Apple, Microsoft a další) a standardů W3C používají přístupové klíče páry kryptografických klíčů. Na rozdíl od uživatelského jména a řetězce znaků, které používáme pro hesla, se pro aplikaci nebo web vytváří pár klíčů (soukromého a veřejného ). Soukromý klíč je bezpečně uložen ve vašem zařízení nebo správci hesel a potvrzuje vaši identitu. Veřejný klíč je sdílen s aplikací nebo webovým serverem. Pomocí odpovídajících klíčů se můžete okamžitě zaregistrovat a přihlásit."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Vylepšené zabezpečení účtu"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Každý klíč je propojen výhradně s aplikací nebo webem, pro které byl vytvořen, takže se nikdy nemůžete omylem přihlásit k podvodné aplikaci nebo webu. Protože na serverech jsou uloženy pouze veřejné klíče, je hackování navíc mnohem obtížnější."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Bezproblémový přechod"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Ačkoliv směřujeme k budoucnosti bez hesel, vedle přístupových klíčů budou stále k dispozici i hesla."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"Určete, kam ukládat <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Vyberte správce hesel k uložení svých údajů, abyste se příště mohli přihlásit rychleji"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vytvořit přístupový klíč pro aplikaci <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Uložit heslo pro aplikaci <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Uložit přihlašovací údaje pro aplikaci <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"přístupový klíč"</string>
<string name="password" msgid="6738570945182936667">"heslo"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"přístupové klíče"</string>
+ <string name="passwords" msgid="5419394230391253816">"hesla"</string>
<string name="sign_ins" msgid="4710739369149469208">"přihlášení"</string>
<string name="sign_in_info" msgid="2627704710674232328">"přihlašovací údaje"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Uložit <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> do"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Vytvořit přístupový klíč v jiném zařízení?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Používat <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pro všechna přihlášení?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Tento správce hesel bude ukládat vaše hesla a přístupové klíče, abyste se mohli snadno přihlásit"</string>
<string name="set_as_default" msgid="4415328591568654603">"Nastavit jako výchozí"</string>
<string name="use_once" msgid="9027366575315399714">"Použít jednou"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Hesla: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Přístupové klíče: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml
index 4c68886..86135b0 100644
--- a/packages/CredentialManager/res/values-da/strings.xml
+++ b/packages/CredentialManager/res/values-da/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Annuller"</string>
<string name="string_continue" msgid="1346732695941131882">"Fortsæt"</string>
<string name="string_more_options" msgid="7990658711962795124">"Flere valgmuligheder"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Øget beskyttelse med adgangsnøgler"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Når du bruger adgangsnøgler, behøver du ikke at oprette eller huske avancerede adgangskoder"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Adgangsnøgler er krypterede digitale nøgler, som du opretter ved hjælp af fingeraftryk, ansigtsgenkendelse eller skærmlås"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Disse gemmes i en adgangskodeadministrator, så du kan logge ind på andre enheder"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Vælg, hvor du vil gemme dine <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Vælg en adgangskodeadministrator for at gemme dine oplysninger, så du kan logge ind hurtigere næste gang"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vil du oprette en adgangsnøgle til <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Vil du gemme adgangskoden til <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vil du gemme loginoplysningerne til <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"adgangsnøgle"</string>
<string name="password" msgid="6738570945182936667">"adgangskode"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"adgangsnøgler"</string>
+ <string name="passwords" msgid="5419394230391253816">"adgangskoder"</string>
<string name="sign_ins" msgid="4710739369149469208">"loginmetoder"</string>
<string name="sign_in_info" msgid="2627704710674232328">"loginoplysninger"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Gem <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> her:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Vil du oprette en adgangsnøgle på en anden enhed?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Vil du bruge <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> til alle dine loginmetoder?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Denne adgangskodeadministrator gemmer dine adgangskoder og adgangsnøgler for at hjælpe dig med nemt at logge ind"</string>
<string name="set_as_default" msgid="4415328591568654603">"Angiv som standard"</string>
<string name="use_once" msgid="9027366575315399714">"Brug én gang"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> adgangskoder • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> adgangsnøgler"</string>
diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml
index d26f430..8da48c1 100644
--- a/packages/CredentialManager/res/values-de/strings.xml
+++ b/packages/CredentialManager/res/values-de/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Abbrechen"</string>
<string name="string_continue" msgid="1346732695941131882">"Weiter"</string>
<string name="string_more_options" msgid="7990658711962795124">"Weitere Optionen"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Mehr Sicherheit mit Passkeys"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Mit Passkeys musst du keine komplizierten Passwörter erstellen oder dir merken"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Passkeys sind verschlüsselte digitale Schlüssel, die du mithilfe deines Fingerabdrucks, Gesichts oder deiner Displaysperre erstellst"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Sie werden in einem Passwortmanager gespeichert, damit du dich auf anderen Geräten anmelden kannst"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Wähle aus, wo deine <xliff:g id="CREATETYPES">%1$s</xliff:g> gespeichert werden sollen"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Du kannst einen Passwortmanager auswählen, um deine Anmeldedaten zu speichern, damit du dich nächstes Mal schneller anmelden kannst"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Passkey für <xliff:g id="APPNAME">%1$s</xliff:g> erstellen?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Passwort für <xliff:g id="APPNAME">%1$s</xliff:g> speichern?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Anmeldedaten für <xliff:g id="APPNAME">%1$s</xliff:g> speichern?"</string>
<string name="passkey" msgid="632353688396759522">"Passkey"</string>
<string name="password" msgid="6738570945182936667">"Passwort"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"Passkeys"</string>
+ <string name="passwords" msgid="5419394230391253816">"Passwörter"</string>
<string name="sign_ins" msgid="4710739369149469208">"Anmeldungen"</string>
<string name="sign_in_info" msgid="2627704710674232328">"Anmeldedaten"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> speichern unter"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Passkey auf einem anderen Gerät erstellen?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> für alle Anmeldungen verwenden?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Mit diesem Passwortmanager werden deine Passwörter und Passkeys gespeichert, damit du dich problemlos anmelden kannst"</string>
<string name="set_as_default" msgid="4415328591568654603">"Als Standard festlegen"</string>
<string name="use_once" msgid="9027366575315399714">"Einmal verwenden"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> Passwörter • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> Passkeys"</string>
diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml
index 3fb6506..f99b5f0 100644
--- a/packages/CredentialManager/res/values-el/strings.xml
+++ b/packages/CredentialManager/res/values-el/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Ακύρωση"</string>
<string name="string_continue" msgid="1346732695941131882">"Συνέχεια"</string>
<string name="string_more_options" msgid="7990658711962795124">"Περισσότερες επιλογές"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Μεγαλύτερη ασφάλεια με κλειδιά πρόσβασης"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Με τα κλειδιά πρόσβασης, δεν χρειάζεται να δημιουργείτε ή να θυμάστε σύνθετους κωδικούς πρόσβασης."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Τα κλειδιά πρόσβασης είναι κρυπτογραφημένα ψηφιακά κλειδιά που δημιουργείτε χρησιμοποιώντας το δακτυλικό σας αποτύπωμα, το πρόσωπο ή το κλείδωμα οθόνης."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Αποθηκεύονται σε ένα πρόγραμμα διαχείρισης κωδικών πρόσβασης, για να μπορείτε να συνδέεστε από άλλες συσκευές."</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"Επιλέξτε πού θα αποθηκεύονται τα <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Επιλέξτε ένα πρόγραμμα διαχείρισης κωδικών πρόσβασης για να αποθηκεύσετε τα στοιχεία σας και να συνδεθείτε πιο γρήγορα την επόμενη φορά."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Δημιουργία κλειδιού πρόσβασης για <xliff:g id="APPNAME">%1$s</xliff:g>;"</string>
diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml
index 0bccea1..76b58c7 100644
--- a/packages/CredentialManager/res/values-en-rAU/strings.xml
+++ b/packages/CredentialManager/res/values-en-rAU/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
<string name="string_continue" msgid="1346732695941131882">"Continue"</string>
<string name="string_more_options" msgid="7990658711962795124">"More options"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Learn more"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Safer with passkeys"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"With passkeys, you don’t need to create or remember complex passwords"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Passkeys are encrypted digital keys that you create using your fingerprint, face or screen lock"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"They are saved to a password manager, so that you can sign in on other devices"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"More about passkeys"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Passwordless technology"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Passkeys allow you to sign in without relying on passwords. You just need to use your fingerprint, face recognition, PIN or swipe pattern to verify your identity and create a passkey."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Public key cryptography"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Based on FIDO Alliance (which includes Google, Apple, Microsoft and more) and W3C standards, passkeys use cryptographic key pairs. Unlike the username and string of characters that we use for passwords, a private-public key pair is created for an app or website. The private key is safely stored on your device or password manager and it confirms your identity. The public key is shared with the app or website server. With corresponding keys, you can instantly register and sign in."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Improved account security"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Each key is exclusively linked with the app or website they were created for, so you can never sign in to a fraudulent app or website by mistake. Plus, with servers only keeping public keys, hacking is a lot harder."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Seamless transition"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-en-rCA/strings.xml b/packages/CredentialManager/res/values-en-rCA/strings.xml
index 38637df..5277f67 100644
--- a/packages/CredentialManager/res/values-en-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-en-rCA/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
<string name="string_continue" msgid="1346732695941131882">"Continue"</string>
<string name="string_more_options" msgid="7990658711962795124">"More options"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Learn more"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Safer with passkeys"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"With passkeys, you don’t need to create or remember complex passwords"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Passkeys are encrypted digital keys you create using your fingerprint, face, or screen lock"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"They are saved to a password manager, so you can sign in on other devices"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"More about passkeys"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Passwordless technology"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Passkeys allow you to sign in without relying on passwords. You just need to use your fingerprint, face recognition, PIN, or swipe pattern to verify your identity and create a passkey."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Public key cryptography"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Based on FIDO Alliance (which includes Google, Apple, Microsoft, and more) and W3C standards, passkeys use cryptographic key pairs. Unlike the username and string of characters we use for passwords, a private-public key pair is created for an app or website. The private key is safely stored on your device or password manager and it confirms your identity. The public key is shared with the app or website server. With corresponding keys, you can instantly register and sign in."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Improved account security"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Each key is exclusively linked with the app or website they were created for, so you can never sign in to a fraudulent app or website by mistake. Plus, with servers only keeping public keys, hacking is a lot harder."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Seamless transition"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml
index 0bccea1..76b58c7 100644
--- a/packages/CredentialManager/res/values-en-rGB/strings.xml
+++ b/packages/CredentialManager/res/values-en-rGB/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
<string name="string_continue" msgid="1346732695941131882">"Continue"</string>
<string name="string_more_options" msgid="7990658711962795124">"More options"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Learn more"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Safer with passkeys"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"With passkeys, you don’t need to create or remember complex passwords"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Passkeys are encrypted digital keys that you create using your fingerprint, face or screen lock"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"They are saved to a password manager, so that you can sign in on other devices"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"More about passkeys"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Passwordless technology"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Passkeys allow you to sign in without relying on passwords. You just need to use your fingerprint, face recognition, PIN or swipe pattern to verify your identity and create a passkey."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Public key cryptography"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Based on FIDO Alliance (which includes Google, Apple, Microsoft and more) and W3C standards, passkeys use cryptographic key pairs. Unlike the username and string of characters that we use for passwords, a private-public key pair is created for an app or website. The private key is safely stored on your device or password manager and it confirms your identity. The public key is shared with the app or website server. With corresponding keys, you can instantly register and sign in."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Improved account security"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Each key is exclusively linked with the app or website they were created for, so you can never sign in to a fraudulent app or website by mistake. Plus, with servers only keeping public keys, hacking is a lot harder."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Seamless transition"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml
index 0bccea1..76b58c7 100644
--- a/packages/CredentialManager/res/values-en-rIN/strings.xml
+++ b/packages/CredentialManager/res/values-en-rIN/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
<string name="string_continue" msgid="1346732695941131882">"Continue"</string>
<string name="string_more_options" msgid="7990658711962795124">"More options"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Learn more"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Safer with passkeys"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"With passkeys, you don’t need to create or remember complex passwords"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Passkeys are encrypted digital keys that you create using your fingerprint, face or screen lock"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"They are saved to a password manager, so that you can sign in on other devices"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"More about passkeys"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Passwordless technology"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Passkeys allow you to sign in without relying on passwords. You just need to use your fingerprint, face recognition, PIN or swipe pattern to verify your identity and create a passkey."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Public key cryptography"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Based on FIDO Alliance (which includes Google, Apple, Microsoft and more) and W3C standards, passkeys use cryptographic key pairs. Unlike the username and string of characters that we use for passwords, a private-public key pair is created for an app or website. The private key is safely stored on your device or password manager and it confirms your identity. The public key is shared with the app or website server. With corresponding keys, you can instantly register and sign in."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Improved account security"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Each key is exclusively linked with the app or website they were created for, so you can never sign in to a fraudulent app or website by mistake. Plus, with servers only keeping public keys, hacking is a lot harder."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Seamless transition"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-en-rXC/strings.xml b/packages/CredentialManager/res/values-en-rXC/strings.xml
index 081cfe8..9ea9cf5 100644
--- a/packages/CredentialManager/res/values-en-rXC/strings.xml
+++ b/packages/CredentialManager/res/values-en-rXC/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
<string name="string_continue" msgid="1346732695941131882">"Continue"</string>
<string name="string_more_options" msgid="7990658711962795124">"More options"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Learn more"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Safer with passkeys"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"With passkeys, you don’t need to create or remember complex passwords"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Passkeys are encrypted digital keys you create using your fingerprint, face, or screen lock"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"They are saved to a password manager, so you can sign in on other devices"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"More about passkeys"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Passwordless technology"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Passkeys allow you to sign in without relying on passwords. You just need to use your fingerprint, face recognition, PIN, or swipe pattern to verify your identity and create a passkey."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Public key cryptography"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Based on FIDO Alliance (which includes Google, Apple, Microsoft, and more) and W3C standards, passkeys use cryptographic key pairs. Unlike the username and string of characters we use for passwords, a private-public key pair is created for an app or website. The private key is safely stored on your device or password manager and it confirms your identity. The public key is shared with the app or website server. With corresponding keys, you can instantly register and sign in."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Improved account security"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Each key is exclusively linked with the app or website they were created for, so you can never sign in to a fraudulent app or website by mistake. Plus, with servers only keeping public keys, hacking is a lot harder."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Seamless transition"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml
index 9a6c3f3..5ea787f 100644
--- a/packages/CredentialManager/res/values-es-rUS/strings.xml
+++ b/packages/CredentialManager/res/values-es-rUS/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
<string name="string_more_options" msgid="7990658711962795124">"Más opciones"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Más seguridad con llaves de acceso"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Con las llaves de acceso, no es necesario crear ni recordar contraseñas complejas"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Las llaves de acceso son claves digitales encriptadas que puedes crear usando tu huella dactilar, rostro o bloqueo de pantalla"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Se guardan en un administrador de contraseñas para que puedas acceder en otros dispositivos"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Elige dónde guardar tus <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un administrador de contraseñas para guardar tu información y acceder más rápido la próxima vez"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"¿Quieres crear una llave de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"¿Quieres guardar la contraseña para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"¿Quieres guardar la información de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contraseña"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"llaves de acceso"</string>
+ <string name="passwords" msgid="5419394230391253816">"contraseñas"</string>
<string name="sign_ins" msgid="4710739369149469208">"accesos"</string>
<string name="sign_in_info" msgid="2627704710674232328">"información de acceso"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Guardar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> en"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"¿Quieres crear una llave de acceso en otro dispositivo?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"¿Quieres usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos tus accesos?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Este administrador de contraseñas almacenará tus contraseñas y llaves de acceso para ayudarte a acceder fácilmente"</string>
<string name="set_as_default" msgid="4415328591568654603">"Establecer como predeterminado"</string>
<string name="use_once" msgid="9027366575315399714">"Usar una vez"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> llaves de acceso"</string>
diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml
index 73f6e2f..c685bc2 100644
--- a/packages/CredentialManager/res/values-es/strings.xml
+++ b/packages/CredentialManager/res/values-es/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
<string name="string_more_options" msgid="7990658711962795124">"Más opciones"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Más seguridad con las llaves de acceso"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Con las llaves de acceso, no tienes que crear ni recordar contraseñas complicadas"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Las llaves de acceso son claves digitales cifradas que puedes crear con tu huella digital, cara o bloqueo de pantalla"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Se guardan en un gestor de contraseñas para que puedas iniciar sesión en otros dispositivos"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Elige dónde guardar tus <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un gestor de contraseñas para guardar tu información e iniciar sesión más rápido la próxima vez"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"¿Crear llave de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"¿Guardar la contraseña de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"¿Guardar la información de inicio de sesión de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contraseña"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"llaves de acceso"</string>
+ <string name="passwords" msgid="5419394230391253816">"contraseñas"</string>
<string name="sign_ins" msgid="4710739369149469208">"inicios de sesión"</string>
<string name="sign_in_info" msgid="2627704710674232328">"información de inicio de sesión"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Guardar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> en"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"¿Crear llave de acceso en otro dispositivo?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"¿Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos tus inicios de sesión?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Este gestor de contraseñas almacenará tus contraseñas y llaves de acceso para que puedas iniciar sesión fácilmente"</string>
<string name="set_as_default" msgid="4415328591568654603">"Fijar como predeterminado"</string>
<string name="use_once" msgid="9027366575315399714">"Usar una vez"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> llaves de acceso"</string>
diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml
index 597ad7f..30a823d 100644
--- a/packages/CredentialManager/res/values-et/strings.xml
+++ b/packages/CredentialManager/res/values-et/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Tühista"</string>
<string name="string_continue" msgid="1346732695941131882">"Jätka"</string>
<string name="string_more_options" msgid="7990658711962795124">"Rohkem valikuid"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Pääsuvõtmed suurendavad turvalisust"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Pääsuvõtmetega ei pea te looma ega meelde jätma keerukaid paroole"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Pääsuvõtmed on digitaalsed krüpteeritud võtmed, mille loote sõrmejälje, näo või ekraanilukuga"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Need salvestatakse paroolihaldurisse, et saaksite teistes seadmetes sisse logida"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Valige, kuhu soovite oma <xliff:g id="CREATETYPES">%1$s</xliff:g> salvestada"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Valige paroolihaldur, et salvestada oma teave ja järgmisel korral kiiremini sisse logida"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Kas luua rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> jaoks pääsuvõti?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Kas salvestada rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> jaoks parool?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Kas salvestada rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> jaoks sisselogimisandmed?"</string>
<string name="passkey" msgid="632353688396759522">"pääsukood"</string>
<string name="password" msgid="6738570945182936667">"parool"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"pääsuvõtmed"</string>
+ <string name="passwords" msgid="5419394230391253816">"paroolid"</string>
<string name="sign_ins" msgid="4710739369149469208">"sisselogimisandmed"</string>
<string name="sign_in_info" msgid="2627704710674232328">"sisselogimisteave"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Salvestage <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Kas luua pääsuvõti muus seadmes?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Kas kasutada teenust <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kõigi teie sisselogimisandmete puhul?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"See paroolihaldur salvestab teie paroolid ja pääsuvõtmed, et aidata teil hõlpsalt sisse logida"</string>
<string name="set_as_default" msgid="4415328591568654603">"Määra vaikeseadeks"</string>
<string name="use_once" msgid="9027366575315399714">"Kasuta ühe korra"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parooli • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> pääsuvõtit"</string>
diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml
index 0178141..0ca9df7 100644
--- a/packages/CredentialManager/res/values-eu/strings.xml
+++ b/packages/CredentialManager/res/values-eu/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Utzi"</string>
<string name="string_continue" msgid="1346732695941131882">"Egin aurrera"</string>
<string name="string_more_options" msgid="7990658711962795124">"Aukera gehiago"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Sarbide-gako seguruagoak"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Sarbide-gakoei esker, ez duzu pasahitz konplexurik sortu edo gogoratu beharrik"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Hatz-marka, aurpegia edo pantailaren blokeoa erabilita sortzen dituzun giltza digital enkriptatuak dira sarbide-gakoak"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Pasahitz-kudeatzaile batean gordetzen dira, beste gailu batzuen bidez saioa hasi ahal izateko"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Aukeratu non gorde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Hautatu informazioa gordetzeko pasahitz-kudeatzaile bat eta hasi saioa bizkorrago hurrengoan"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> atzitzeko sarbide-gako bat sortu nahi duzu?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioko pasahitza gorde nahi duzu?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioko saioa hasteko informazioa gorde nahi duzu?"</string>
<string name="passkey" msgid="632353688396759522">"sarbide-gakoa"</string>
<string name="password" msgid="6738570945182936667">"pasahitza"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"sarbide-gakoak"</string>
+ <string name="passwords" msgid="5419394230391253816">"pasahitzak"</string>
<string name="sign_ins" msgid="4710739369149469208">"kredentzialak"</string>
<string name="sign_in_info" msgid="2627704710674232328">"saioa hasteko informazioa"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Gorde <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> hemen:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Beste gailu batean sortu nahi duzu sarbide-gakoa?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> erabili nahi duzu kredentzial guztietarako?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Pasahitz-kudeatzaile honek pasahitzak eta sarbide-gakoak gordeko ditu saioa erraz has dezazun"</string>
<string name="set_as_default" msgid="4415328591568654603">"Ezarri lehenetsi gisa"</string>
<string name="use_once" msgid="9027366575315399714">"Erabili behin"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> pasahitz • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> sarbide-gako"</string>
diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml
index 1b11819..47325b5 100644
--- a/packages/CredentialManager/res/values-fa/strings.xml
+++ b/packages/CredentialManager/res/values-fa/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"لغو"</string>
<string name="string_continue" msgid="1346732695941131882">"ادامه"</string>
<string name="string_more_options" msgid="7990658711962795124">"گزینههای بیشتر"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"فضایی ایمنتر با گذرکلید"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"با گذرکلیدها، لازم نیست گذرواژه پیچیدهای بسازید یا آن را بهخاطر بسپارید"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"گذرکلیدها کلیدهای دیجیتالی رمزگذاریشدهای هستند که بااستفاده از اثر انگشت، چهره، یا قفل صفحه ایجاد میکنید"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"گذرکلیدها در «مدیر گذرواژه» ذخیره میشود تا بتوانید در دستگاههای دیگر به سیستم وارد شوید"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"جایی را برای ذخیره کردن <xliff:g id="CREATETYPES">%1$s</xliff:g> انتخاب کنید"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"مدیر گذرواژهای انتخاب کنید تا اطلاعاتتان ذخیره شود و دفعه بعدی سریعتر به سیستم وارد شوید"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"برای <xliff:g id="APPNAME">%1$s</xliff:g> گذرکلید ایجاد شود؟"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"گذرواژه <xliff:g id="APPNAME">%1$s</xliff:g> ذخیره شود؟"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"اطلاعات ورود به سیستم <xliff:g id="APPNAME">%1$s</xliff:g> ذخیره شود؟"</string>
<string name="passkey" msgid="632353688396759522">"گذرکلید"</string>
<string name="password" msgid="6738570945182936667">"گذرواژه"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"گذرکلیدها"</string>
+ <string name="passwords" msgid="5419394230391253816">"گذرواژهها"</string>
<string name="sign_ins" msgid="4710739369149469208">"ورود به سیستمها"</string>
<string name="sign_in_info" msgid="2627704710674232328">"اطلاعات ورود به سیستم"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"ذخیره <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> در"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"گذرکلید در دستگاه دیگر ایجاد شود؟"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"از <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> برای همه ورود به سیستمها استفاده شود؟"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"این مدیر گذرواژه گذرکلیدها و گذرواژههای شما را ذخیره خواهد کرد تا بهراحتی بتوانید به سیستم وارد شوید"</string>
<string name="set_as_default" msgid="4415328591568654603">"تنظیم بهعنوان پیشفرض"</string>
<string name="use_once" msgid="9027366575315399714">"یکبار استفاده"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> گذرواژه • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> گذرکلید"</string>
diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml
index bdb55fb..6dff909 100644
--- a/packages/CredentialManager/res/values-fi/strings.xml
+++ b/packages/CredentialManager/res/values-fi/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Peru"</string>
<string name="string_continue" msgid="1346732695941131882">"Jatka"</string>
<string name="string_more_options" msgid="7990658711962795124">"Lisäasetukset"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Turvallisuutta avainkoodeilla"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Kun käytät avainkoodeja, sinun ei tarvitse luoda tai muistaa monimutkaisia salasanoja"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Avainkoodit ovat salattuja digitaalisia avaimia, joita voit luoda sormenjäljen, kasvojen tai näytön lukituksen avulla"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Ne tallennetaan salasanojen ylläpito-ohjelmaan, jotta voit kirjautua sisään muilla laitteilla"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Valitse, minne <xliff:g id="CREATETYPES">%1$s</xliff:g> tallennetaan"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Valitse salasanojen ylläpitotyökalu, niin voit tallentaa tietosi ja kirjautua ensi kerralla nopeammin sisään"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Luodaanko avainkoodi (<xliff:g id="APPNAME">%1$s</xliff:g>)?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Tallennetaanko salasana (<xliff:g id="APPNAME">%1$s</xliff:g>)?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Tallennetaanko kirjautumistiedot (<xliff:g id="APPNAME">%1$s</xliff:g>)?"</string>
<string name="passkey" msgid="632353688396759522">"avainkoodi"</string>
<string name="password" msgid="6738570945182936667">"salasana"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"avainkoodit"</string>
+ <string name="passwords" msgid="5419394230391253816">"salasanat"</string>
<string name="sign_ins" msgid="4710739369149469208">"sisäänkirjautumiset"</string>
<string name="sign_in_info" msgid="2627704710674232328">"kirjautumistiedot"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Tallenna <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> tänne:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Luodaanko avainkoodi toisella laitteella?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Otetaanko <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> käyttöön kaikissa sisäänkirjautumisissa?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Tämä salasanojen ylläpitotyökalu tallentaa salasanat ja avainkoodit, jotta voit kirjautua helposti sisään"</string>
<string name="set_as_default" msgid="4415328591568654603">"Aseta oletukseksi"</string>
<string name="use_once" msgid="9027366575315399714">"Käytä kerran"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> salasanaa • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> avainkoodia"</string>
diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml
index 1e4b009..c501178 100644
--- a/packages/CredentialManager/res/values-fr-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Annuler"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuer"</string>
<string name="string_more_options" msgid="7990658711962795124">"Autres options"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Une sécurité accrue grâce aux clés d\'accès"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Avec les clés d\'accès, nul besoin de créer ou de mémoriser des mots de passe complexes"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Les clés d\'accès sont des clés numériques chiffrées que vous créez en utilisant votre empreinte digitale, votre visage ou le verrouillage de votre écran"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Ils sont enregistrés dans un gestionnaire de mots de passe pour vous permettre de vous connecter sur d\'autres appareils"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Choisir où sauvegarder vos <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Sélectionnez un gestionnaire de mots de passe pour enregistrer vos renseignements et vous connecter plus rapidement la prochaine fois"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Créer une clé d\'accès pour <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Enregistrer le mot de passe pour <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Enregistrer les renseignements de connexion pour <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
<string name="password" msgid="6738570945182936667">"mot de passe"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"clés d\'accès"</string>
+ <string name="passwords" msgid="5419394230391253816">"mots de passe"</string>
<string name="sign_ins" msgid="4710739369149469208">"connexions"</string>
<string name="sign_in_info" msgid="2627704710674232328">"renseignements de connexion"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Enregistrer <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> dans"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Créer une clé d\'accès dans un autre appareil?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Utiliser <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pour toutes vos connexions?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Ce gestionnaire de mots de passe stockera vos mots de passe et vos clés d\'accès pour vous permettre de vous connecter facilement"</string>
<string name="set_as_default" msgid="4415328591568654603">"Définir par défaut"</string>
<string name="use_once" msgid="9027366575315399714">"Utiliser une fois"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clés d\'accès"</string>
diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml
index 7073ca6..05a0601 100644
--- a/packages/CredentialManager/res/values-fr/strings.xml
+++ b/packages/CredentialManager/res/values-fr/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Annuler"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuer"</string>
<string name="string_more_options" msgid="7990658711962795124">"Autres options"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Sécurité renforcée grâce aux clés d\'accès"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Avec les clés d\'accès, plus besoin de créer ni de mémoriser des mots de passe complexes"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Les clés d\'accès sont des clés numériques chiffrées que vous créez à l\'aide de votre empreinte digitale, de votre visage ou du verrouillage de l\'écran"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Elles sont enregistrées dans un gestionnaire de mots de passe pour que vous puissiez vous connecter sur d\'autres appareils"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Choisissez où enregistrer vos <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Sélectionnez un gestionnaire de mots de passe pour enregistrer vos informations et vous connecter plus rapidement la prochaine fois"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Créer une clé d\'accès pour <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Enregistrer le mot de passe pour <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Enregistrer les informations de connexion pour <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
<string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
<string name="password" msgid="6738570945182936667">"mot de passe"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"clés d\'accès"</string>
+ <string name="passwords" msgid="5419394230391253816">"mots de passe"</string>
<string name="sign_ins" msgid="4710739369149469208">"connexions"</string>
<string name="sign_in_info" msgid="2627704710674232328">"informations de connexion"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Enregistrer la <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> dans"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Créer une clé d\'accès sur un autre appareil ?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Utiliser <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pour toutes vos connexions ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Ce gestionnaire de mots de passe stockera vos mots de passe et clés d\'accès pour vous permettre de vous connecter facilement"</string>
<string name="set_as_default" msgid="4415328591568654603">"Définir par défaut"</string>
<string name="use_once" msgid="9027366575315399714">"Utiliser une fois"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mot(s) de passe • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clé(s) d\'accès"</string>
diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml
index 5b6e719..4fb09cc 100644
--- a/packages/CredentialManager/res/values-gl/strings.xml
+++ b/packages/CredentialManager/res/values-gl/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
<string name="string_more_options" msgid="7990658711962795124">"Máis opcións"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Máis protección coas claves de acceso"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Cunha clave de acceso, non é necesario que crees ou lembres contrasinais complexos"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"As claves de acceso son claves dixitais encriptadas que creas usando a túa impresión dixital, a túa cara ou o teu bloqueo de pantalla"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"As claves de acceso gárdanse nun xestor de contrasinais para que poidas iniciar sesión noutros dispositivos"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Escolle onde queres gardar: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un xestor de contrasinais para gardar a túa información e iniciar sesión máis rápido a próxima vez"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Queres crear unha clave de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Queres gardar o contrasinal de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Queres gardar a información de inicio de sesión de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contrasinal"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"claves de acceso"</string>
+ <string name="passwords" msgid="5419394230391253816">"contrasinais"</string>
<string name="sign_ins" msgid="4710739369149469208">"métodos de inicio de sesión"</string>
<string name="sign_in_info" msgid="2627704710674232328">"información de inicio de sesión"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Gardar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> en"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Queres crear unha clave de acceso noutro dispositivo?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Queres usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> cada vez que inicies sesión?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Este xestor de contrasinais almacenará os contrasinais e as claves de acceso para axudarche a iniciar sesión facilmente"</string>
<string name="set_as_default" msgid="4415328591568654603">"Establecer como predeterminado"</string>
<string name="use_once" msgid="9027366575315399714">"Usar unha vez"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasinais • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> claves de acceso"</string>
diff --git a/packages/CredentialManager/res/values-gu/strings.xml b/packages/CredentialManager/res/values-gu/strings.xml
index fdaf586..6afc6e6 100644
--- a/packages/CredentialManager/res/values-gu/strings.xml
+++ b/packages/CredentialManager/res/values-gu/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"રદ કરો"</string>
<string name="string_continue" msgid="1346732695941131882">"ચાલુ રાખો"</string>
<string name="string_more_options" msgid="7990658711962795124">"વધુ વિકલ્પો"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"વધુ જાણો"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"પાસકી સાથે વધુ સલામત"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"પાસકી હોવાથી, તમારે જટિલ પાસવર્ડ બનાવવાની કે યાદ રાખવાની જરૂર રહેતી નથી"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"પાસકી એ એન્ક્રિપ્ટેડ ડિજિટલ કી છે, જેને તમે તમારી ફિંગરપ્રિન્ટ, ચહેરા અથવા સ્ક્રીન લૉકનો ઉપયોગ કરીને બનાવો છો"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"તેને પાસવર્ડ મેનેજરમાં સાચવવામાં આવે છે, જેથી તમે અન્ય ડિવાઇસમાં સાઇન ઇન ન કરી શકો"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"પાસકી વિશે વધુ"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"પાસવર્ડ રહિત ટેક્નોલોજી"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"પાસકી તમને પાસવર્ડ પર આધાર રાખ્યા વિના સાઇન ઇન કરવાની મંજૂરી આપે છે. તમારી ઓળખની ચકાસણી કરીને તમારી પાસકી બનાવવા માટે, તમારે માત્ર તમારી ફિંગરપ્રિન્ટ, ચહેરાની ઓળખ, પિન અથવા સ્વાઇપ પૅટર્નનો ઉપયોગ કરવાની જરૂર છે."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"જાહેર કીની ક્રિપ્ટોગ્રાફી"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO ભાગીદારી (જેમાં Google, Apple, Microsoft અને વધુનો સમાવેશ થાય છે) અને W3C સ્ટૅન્ડર્ડના આધારે, પાસકી ક્રિપ્ટોગ્રાફિક કીની જોડીનો ઉપયોગ કરે છે. આપણે પાસવર્ડ માટે ઉપયોગ કરીએ છીએ તે વપરાશકર્તાનામ અને અક્ષરોની સ્ટ્રિંગથી વિપરીત, ઍપ કે વેબસાઇટ માટે એક ખાનગી-જાહેર કીની જોડી બને છે. ખાનગી કી તમારા ડિવાઇસ અથવા પાસવર્ડ મેનેજરમાં સુરક્ષિત રીતે સ્ટોર થાય છે અને તે તમારી ઓળખ કન્ફર્મ કરે છે. જાહેર કી ઍપ કે વેબસાઇટના સર્વર સાથે શેર કરવામાં આવે છે. અનુરૂપ કી સાથે, તમે તરત જ રજિસ્ટર અને સાઇન ઇન કરી શકો છો."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"બહેતર બનાવેલી એકાઉન્ટની સુરક્ષા"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"દરેક કીને જે ઍપ અથવા વેબસાઇટ માટે બનાવવામાં આવી હોય તેની સાથે તે વિશેષ રીતે લિંક થયેલી છે, તેથી તમારાથી ક્યારેય ભૂલથી કપટપૂર્ણ ઍપ અથવા વેબસાઇટ પર સાઇન ઇન ન થાય. ઉપરાંત, સર્વર માત્ર જાહેર કી રાખે છે, હૅકિંગ ઘણું મુશ્કેલ છે."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"વિક્ષેપરહિત ટ્રાન્ઝિશન"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"આપણે પાસવર્ડ રહિત ભવિષ્ય તરફ આગળ વધી રહ્યાં છીએ, છતાં પાસકીની સાથોસાથ હજી પણ પાસવર્ડ ઉપલબ્ધ રહેશે."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"તમારી <xliff:g id="CREATETYPES">%1$s</xliff:g> ક્યાં સાચવવી તે પસંદ કરો"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"તમારી માહિતી સાચવવા માટે પાસવર્ડ મેનેજર પસંદ કરો અને આગલી વખતે વધુ ઝડપથી સાઇન ઇન કરો"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> માટે પાસકી બનાવીએ?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> માટે પાસવર્ડ સાચવીએ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> માટે સાઇન-ઇન કરવાની માહિતી સાચવીએ?"</string>
<string name="passkey" msgid="632353688396759522">"પાસકી"</string>
<string name="password" msgid="6738570945182936667">"પાસવર્ડ"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"પાસકી"</string>
+ <string name="passwords" msgid="5419394230391253816">"પાસવર્ડ"</string>
<string name="sign_ins" msgid="4710739369149469208">"સાઇન-ઇન"</string>
<string name="sign_in_info" msgid="2627704710674232328">"સાઇન-ઇન કરવાની માહિતી"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>ને આમાં સાચવો"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"અન્ય ડિવાઇસ પર પાસકી બનાવીએ?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"શું તમારા બધા સાઇન-ઇન માટે <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>નો ઉપયોગ કરીએ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"આ પાસવર્ડ મેનેજર તમને સરળતાથી સાઇન ઇન કરવામાં સહાય કરવા માટે, તમારા પાસવર્ડ અને પાસકી સ્ટોર કરશે"</string>
<string name="set_as_default" msgid="4415328591568654603">"ડિફૉલ્ટ તરીકે સેટ કરો"</string>
<string name="use_once" msgid="9027366575315399714">"એકવાર ઉપયોગ કરો"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> પાસવર્ડ • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> પાસકી"</string>
diff --git a/packages/CredentialManager/res/values-hi/strings.xml b/packages/CredentialManager/res/values-hi/strings.xml
index f75a989..6cc26c5 100644
--- a/packages/CredentialManager/res/values-hi/strings.xml
+++ b/packages/CredentialManager/res/values-hi/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"रद्द करें"</string>
<string name="string_continue" msgid="1346732695941131882">"जारी रखें"</string>
<string name="string_more_options" msgid="7990658711962795124">"ज़्यादा विकल्प"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"पासकी के साथ सुरक्षित रहें"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"पासकी होने पर, आपको जटिल पासवर्ड बनाने या याद रखने की ज़रूरत नहीं पड़ती"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"पासकी, एन्क्रिप्ट (सुरक्षित) की गई डिजिटल की होती हैं. इन्हें फ़िंगरप्रिंट, चेहरे या स्क्रीन लॉक का इस्तेमाल करके बनाया जाता है"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"पासकी को पासवर्ड मैनेजर में सेव किया जाता है, ताकि इनका इस्तेमाल करके आप अन्य डिवाइसों में साइन इन कर सकें"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"चुनें कि अपनी <xliff:g id="CREATETYPES">%1$s</xliff:g> कहां सेव करनी हैं"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"अपनी जानकारी सेव करने के लिए, कोई पासवर्ड मैनेजर चुनें और अगली बार तेज़ी से साइन इन करें"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"क्या आपको <xliff:g id="APPNAME">%1$s</xliff:g> के लिए पासकी बनानी है?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"क्या आपको <xliff:g id="APPNAME">%1$s</xliff:g> के लिए पासवर्ड सेव करना है?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"क्या आपको <xliff:g id="APPNAME">%1$s</xliff:g> के लिए साइन-इन की जानकारी सेव करनी है?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"पासकी"</string>
+ <string name="passwords" msgid="5419394230391253816">"पासवर्ड"</string>
<string name="sign_ins" msgid="4710739369149469208">"साइन इन"</string>
<string name="sign_in_info" msgid="2627704710674232328">"साइन-इन की जानकारी"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> को यहां सेव करें"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"क्या किसी दूसरे डिवाइस में पासकी सेव करनी है?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"क्या आपको साइन इन से जुड़ी सारी जानकारी सेव करने के लिए, <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> का इस्तेमाल करना है?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"यह पासवर्ड मैनेजर, आपके पासवर्ड और पासकी सेव करेगा, ताकि आपको साइन इन करने में आसानी हो"</string>
<string name="set_as_default" msgid="4415328591568654603">"डिफ़ॉल्ट के तौर पर सेट करें"</string>
<string name="use_once" msgid="9027366575315399714">"इसका इस्तेमाल एक बार किया जा सकता है"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> पासकी"</string>
diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml
index 2f9e46b..aff78ff 100644
--- a/packages/CredentialManager/res/values-hr/strings.xml
+++ b/packages/CredentialManager/res/values-hr/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Odustani"</string>
<string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
<string name="string_more_options" msgid="7990658711962795124">"Više opcija"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Saznajte više"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Sigurniji s pristupnim ključevima"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Uz pristupne ključeve ne trebate izrađivati ili pamtiti složene zaporke"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Pristupni ključevi šifrirani su digitalni ključevi koje izrađujete pomoću svojeg otiska prsta, lica ili zaključavanja zaslona"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Spremaju se u upravitelju zaporki kako biste se mogli prijaviti na drugim uređajima"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Više informacija o pristupnim ključevima"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Tehnologija bez upotrebe zaporke"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Pristupni ključevi omogućuju prijavu bez upotrebe zaporki. Treba vam samo otisak prsta, prepoznavanje lica, PIN ili uzorak pokreta prstom da biste potvrdili svoj identitet i izradili pristupni ključ."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Kriptografija javnog ključa"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Na temelju saveza FIDO (koji uključuje Google, Apple, Microsoft i mnoge druge) i standarda W3C pristupni ključevi koriste kriptografske ključeve. Za razliku od korisničkog imena i niza znakova za zaporke, privatno-javni ključ izrađen je za aplikaciju ili web-lokaciju. Privatni ključ pohranjen je na vašem uređaju ili upravitelju zaporki i potvrđuje vaš identitet. Javni se ključ dijeli s poslužiteljem aplikacije ili web-lokacije. Uz odgovarajuće ključeve možete se odmah registrirati i prijaviti."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Poboljšana sigurnost računa"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Svaki ključ povezan isključivo s aplikacijom ili web-lokacijom za koju je izrađen, stoga se nikad ne možete pogreškom prijaviti u prijevarnu aplikaciju ili na web-lokaciju. Osim toga, kad je riječ o poslužiteljima na kojem se nalaze samo javni ključevi, hakiranje je mnogo teže."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Besprijekorni prijelaz"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Kako idemo u smjeru budućnosti bez zaporki, one će i dalje biti dostupne uz pristupne ključeve."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Odaberite mjesto za spremanje: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Odaberite upravitelja zaporki kako biste spremili svoje informacije i drugi se put brže prijavili"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Izraditi pristupni ključ za <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml
index 81616df..f9efc85 100644
--- a/packages/CredentialManager/res/values-hu/strings.xml
+++ b/packages/CredentialManager/res/values-hu/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Mégse"</string>
<string name="string_continue" msgid="1346732695941131882">"Folytatás"</string>
<string name="string_more_options" msgid="7990658711962795124">"További lehetőségek"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Fokozott biztonság – azonosítókulccsal"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Azonosítókulcs birtokában nincs szükség összetett jelszavak létrehozására vagy megjegyzésére"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Az azonosítókulcsok olyan digitális kulcsok, amelyeket ujjlenyomata, arca vagy képernyőzár használatával hoz létre"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Az azonosítókulcsokat a rendszer jelszókezelőbe menti, így más eszközökbe is be tud jelentkezni"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Válassza ki, hogy hova szeretné menteni <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Válasszon jelszókezelőt, hogy menthesse az adatait, és gyorsabban jelentkezhessen be a következő alkalommal."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Létrehoz azonosítókulcsot a következőhöz: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Szeretné elmenteni a(z) <xliff:g id="APPNAME">%1$s</xliff:g> jelszavát?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Menti a bejelentkezési adatokat a következőhöz: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"azonosítókulcs"</string>
<string name="password" msgid="6738570945182936667">"jelszó"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"azonosítókulcsait"</string>
+ <string name="passwords" msgid="5419394230391253816">"jelszavait"</string>
<string name="sign_ins" msgid="4710739369149469208">"bejelentkezési adatok"</string>
<string name="sign_in_info" msgid="2627704710674232328">"bejelentkezési adatok"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> mentése ide:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Egy másik eszközön szeretne azonosítókulcsot létrehozni?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Szeretné a következőt használni az összes bejelentkezési adatához: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Ez a jelszókezelő fogja tárolni a jelszavait és azonosítókulcsait a bejelentkezés megkönnyítése érdekében."</string>
<string name="set_as_default" msgid="4415328591568654603">"Beállítás alapértelmezettként"</string>
<string name="use_once" msgid="9027366575315399714">"Egyszeri használat"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> jelszó, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> azonosítókulcs"</string>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index 782b2d9..790ee8f 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Չեղարկել"</string>
<string name="string_continue" msgid="1346732695941131882">"Շարունակել"</string>
<string name="string_more_options" msgid="7990658711962795124">"Այլ տարբերակներ"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Անցաբառերի հետ ավելի ապահով է"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Անցաբառերի շնորհիվ դուք բարդ գաղտնաբառեր ստեղծելու կամ հիշելու անհրաժեշտություն չեք ունենա"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Անցաբառերը գաղտնագրված թվային բանալիներ են, որոնք ստեղծվում են մատնահետքի, դեմքով ապակողպման կամ էկրանի կողպման օգտագործմամբ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Դուք կարող եք մուտք գործել այլ սարքերում, քանի որ անցաբառերը պահվում են գաղտնաբառերի կառավարիչում"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Նշեք, թե որտեղ եք ուզում պահել ձեր <xliff:g id="CREATETYPES">%1$s</xliff:g>ը"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Ընտրեք գաղտնաբառերի կառավարիչ՝ ձեր տեղեկությունները պահելու և հաջորդ անգամ ավելի արագ մուտք գործելու համար"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Ստեղծե՞լ անցաբառ «<xliff:g id="APPNAME">%1$s</xliff:g>» հավելվածի համար"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Պահե՞լ «<xliff:g id="APPNAME">%1$s</xliff:g>» հավելվածի գաղտնաբառը"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Պահե՞լ «<xliff:g id="APPNAME">%1$s</xliff:g>» հավելվածի մուտքի տվյալները"</string>
<string name="passkey" msgid="632353688396759522">"անցաբառ"</string>
<string name="password" msgid="6738570945182936667">"գաղտնաբառ"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"անցաբառեր"</string>
+ <string name="passwords" msgid="5419394230391253816">"գաղտնաբառեր"</string>
<string name="sign_ins" msgid="4710739369149469208">"մուտք"</string>
<string name="sign_in_info" msgid="2627704710674232328">"մուտքի տվյալներ"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Պահել <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>ն այստեղ՝"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Ստեղծե՞լ անցաբառ այլ սարքում"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Միշտ մուտք գործե՞լ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածի միջոցով"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Գաղտնաբառերի այս կառավարիչը կպահի ձեր գաղտնաբառերն ու անցաբառերը՝ օգնելու ձեզ հեշտությամբ մուտք գործել հաշիվ"</string>
<string name="set_as_default" msgid="4415328591568654603">"Նշել որպես կանխադրված"</string>
<string name="use_once" msgid="9027366575315399714">"Օգտագործել մեկ անգամ"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> գաղտնաբառ • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> անցաբառ"</string>
diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml
index f16a321..b3d42d0 100644
--- a/packages/CredentialManager/res/values-in/strings.xml
+++ b/packages/CredentialManager/res/values-in/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Batal"</string>
<string name="string_continue" msgid="1346732695941131882">"Lanjutkan"</string>
<string name="string_more_options" msgid="7990658711962795124">"Opsi lainnya"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Lebih aman dengan kunci sandi"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Dengan kunci sandi, Anda tidak perlu membuat atau mengingat sandi yang rumit"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Kunci sandi adalah kunci digital terenkripsi yang Anda buat menggunakan sidik jari, wajah, atau kunci layar"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Kunci sandi disimpan ke pengelola sandi, sehingga Anda dapat login di perangkat lainnya"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Pilih tempat penyimpanan <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Pilih pengelola sandi untuk menyimpan info Anda dan login lebih cepat pada waktu berikutnya"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Buat kunci sandi untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Simpan sandi untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Simpan info login untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"kunci sandi"</string>
<string name="password" msgid="6738570945182936667">"sandi"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"kunci sandi"</string>
+ <string name="passwords" msgid="5419394230391253816">"sandi"</string>
<string name="sign_ins" msgid="4710739369149469208">"login"</string>
<string name="sign_in_info" msgid="2627704710674232328">"info login"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Simpan <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ke"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Buat kunci sandi di perangkat lain?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Gunakan <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> untuk semua info login Anda?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Pengelola sandi ini akan menyimpan sandi dan kunci sandi untuk membantu Anda login dengan mudah"</string>
<string name="set_as_default" msgid="4415328591568654603">"Setel sebagai default"</string>
<string name="use_once" msgid="9027366575315399714">"Gunakan sekali"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> sandi • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> kunci sandi"</string>
diff --git a/packages/CredentialManager/res/values-is/strings.xml b/packages/CredentialManager/res/values-is/strings.xml
index 170cd9a..d13c991 100644
--- a/packages/CredentialManager/res/values-is/strings.xml
+++ b/packages/CredentialManager/res/values-is/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Hætta við"</string>
<string name="string_continue" msgid="1346732695941131882">"Áfram"</string>
<string name="string_more_options" msgid="7990658711962795124">"Fleiri valkostir"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Aukið öryggi með aðgangslyklum"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Með aðgangslyklum þarftu hvorki að búa til né muna flókin aðgangsorð"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Aðgangslyklar eru dulkóðaðir stafrænir lyklar sem þú býrð til með fingrafarinu þínu, andliti eða skjálás."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Þeir eru vistaðir í aðgangsorðastjórnun svo þú getir skráð þig inn í öðrum tækjum"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Veldu hvar þú vilt vista <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Veldu aðgangsorðastjórnun til að vista upplýsingarnar og vera fljótari að skrá þig inn næst"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Viltu búa til aðgangslykil fyrir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Viltu vista aðgangsorð fyrir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Viltu vista innskráningarupplýsingar fyrir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"aðgangslykill"</string>
<string name="password" msgid="6738570945182936667">"aðgangsorð"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"aðgangslyklar"</string>
+ <string name="passwords" msgid="5419394230391253816">"aðgangsorð"</string>
<string name="sign_ins" msgid="4710739369149469208">"innskráningar"</string>
<string name="sign_in_info" msgid="2627704710674232328">"innskráningarupplýsingar"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Vista <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> í"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Viltu búa til aðgangslykil í öðru tæki?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Nota <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> fyrir allar innskráningar?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Þessi aðgangsorðastjórnun vistar aðgangsorð og aðgangslykla til að auðvelda þér að skrá þig inn"</string>
<string name="set_as_default" msgid="4415328591568654603">"Stilla sem sjálfgefið"</string>
<string name="use_once" msgid="9027366575315399714">"Nota einu sinni"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> aðgangsorð • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> aðgangslyklar"</string>
diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml
index 0707256..c28659b 100644
--- a/packages/CredentialManager/res/values-it/strings.xml
+++ b/packages/CredentialManager/res/values-it/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"Annulla"</string>
<string name="string_continue" msgid="1346732695941131882">"Continua"</string>
<string name="string_more_options" msgid="7990658711962795124">"Altre opzioni"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Scopri di più"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Più al sicuro con le passkey"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Con le passkey non è necessario creare o ricordare password complesse"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Le passkey sono chiavi digitali criptate che crei usando la tua impronta, il tuo volto o il blocco schermo"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Vengono salvate in un gestore delle password, così potrai accedere su altri dispositivi"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Scopri di più sulle passkey"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Tecnologia senza password"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Le passkey ti consentono di accedere senza usare le password. Devi soltanto usare la tua impronta, il riconoscimento del volto, il tuo PIN o la tua sequenza per verificare la tua identità e creare una passkey."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Crittografia a chiave pubblica"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Basate su standard FIDO Alliance (che include Google, Apple, Microsoft e non solo) e W3C, le passkey usano coppie di chiavi di crittografia. Diversamente dal nome utente e dalla stringa di caratteri usata per le password, per un\'app o un sito web viene creata una coppia di chiavi (privata e pubblica). La chiave privata viene memorizzata in sicurezza sul dispositivo o nel gestore delle password e conferma la tua identità. La chiave pubblica viene condivisa con il server dell\'app o del sito. Con chiavi corrispondenti puoi registrarti e accedere subito."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Sicurezza degli account migliorata"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Ogni chiave è collegata in modo esclusivo all\'app o al sito web per cui è stata creata, quindi non puoi mai accedere a un\'app o un sito web fraudolenti per sbaglio. Inoltre, le compromissioni diventano molto più difficili perché i server conservano soltanto le chiavi pubbliche."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Transizione senza interruzioni"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Mentre ci dirigiamo verso un futuro senza password, queste ultime saranno ancora disponibili insieme alle passkey."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"Scegli dove salvare: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Seleziona un gestore delle password per salvare i tuoi dati e accedere più velocemente la prossima volta"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vuoi creare una passkey per <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Vuoi salvare la password di <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vuoi salvare i dati di accesso di <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"passkey"</string>
+ <string name="passwords" msgid="5419394230391253816">"password"</string>
<string name="sign_ins" msgid="4710739369149469208">"accessi"</string>
<string name="sign_in_info" msgid="2627704710674232328">"dati di accesso"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Salva <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> in"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Creare la passkey in un altro dispositivo?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Vuoi usare <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> per tutti gli accessi?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Questo gestore delle password archivierà le password e le passkey per aiutarti ad accedere facilmente"</string>
<string name="set_as_default" msgid="4415328591568654603">"Imposta come valore predefinito"</string>
<string name="use_once" msgid="9027366575315399714">"Usa una volta"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> password • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkey"</string>
diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml
index be1af3b..1637a94 100644
--- a/packages/CredentialManager/res/values-iw/strings.xml
+++ b/packages/CredentialManager/res/values-iw/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"ביטול"</string>
<string name="string_continue" msgid="1346732695941131882">"המשך"</string>
<string name="string_more_options" msgid="7990658711962795124">"אפשרויות נוספות"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"בטוח יותר להשתמש במפתחות גישה"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"עם מפתחות הגישה לא צריך יותר ליצור או לזכור סיסמאות מורכבות"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"מפתחות גישה הם מפתחות דיגיטליים מוצפנים שניתן ליצור באמצעות טביעת האצבע, זיהוי הפנים או נעילת המסך"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"מפתחות הגישה והסיסמאות נשמרים במנהל הסיסמאות כך שניתן להיכנס לחשבון במכשירים אחרים"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"בחירת המקום לשמירה של <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"אפשר לבחור באחד משירותי ניהול הסיסמאות כדי לשמור את הפרטים ולהיכנס לחשבון מהר יותר בפעם הבאה"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ליצור מפתח גישה ל-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
index 9bdb9b0..3aab2e9f 100644
--- a/packages/CredentialManager/res/values-ja/strings.xml
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"キャンセル"</string>
<string name="string_continue" msgid="1346732695941131882">"続行"</string>
<string name="string_more_options" msgid="7990658711962795124">"その他のオプション"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"詳細"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"パスキーでより安全に"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"パスキーがあれば、複雑なパスワードを作成したり覚えたりする必要はありません"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"パスキーは、指紋認証、顔認証、または画面ロックを使って作成される暗号化されたデジタルキーです"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"パスワード マネージャーに保存され、他のデバイスでもログインできます"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"パスキーの詳細"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"パスワードレス テクノロジー"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"パスキーを利用すると、ログインにパスワードは必要なくなります。指紋認証、顔認証、PIN を使用するか、パターンをスワイプするだけで、本人確認とパスキーの作成を行えます。"</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"公開鍵暗号"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO アライアンス(Google、Apple、Microsoft などで構成)と W3C 標準に基づき、パスキーでは暗号鍵ペアを使用します。ユーザー名や、パスワードに使う文字列と異なり、アプリやウェブサイトに対して秘密 / 公開鍵ペアが作成されます。秘密鍵はデバイスまたはパスワード マネージャーに安全に保存され、この鍵で本人確認を行います。公開鍵はアプリまたはウェブサイトのサーバーと共有されます。対応する鍵を使うことで、すばやく登録してログインできます。"</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"アカウントのセキュリティを強化"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"作成された各鍵は、対象となるアプリまたはウェブサイトのみとリンクされるため、間違って不正なアプリやウェブサイトにログインすることはありません。さらに、公開鍵はサーバーのみに保存されるため、ハッキングのリスクも大幅に抑えられます。"</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"シームレスな移行"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"将来的にパスワードレスに移行するにあたり、パスワードもパスキーと並行して引き続きご利用いただけます。"</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> の保存先の選択"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"パスワード マネージャーを選択して情報を保存しておくと、次回からすばやくログインできます"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスキーを作成しますか?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスワードを保存しますか?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string>
<string name="passkey" msgid="632353688396759522">"パスキー"</string>
<string name="password" msgid="6738570945182936667">"パスワード"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"パスキー"</string>
+ <string name="passwords" msgid="5419394230391253816">"パスワード"</string>
<string name="sign_ins" msgid="4710739369149469208">"ログイン"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ログイン情報"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>の保存先"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"別のデバイスにパスキーを作成しますか?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ログインのたびに <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> を使用しますか?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"このパスワード マネージャーに、パスワードやパスキーが保存され、簡単にログインできるようになります"</string>
<string name="set_as_default" msgid="4415328591568654603">"デフォルトに設定"</string>
<string name="use_once" msgid="9027366575315399714">"1 回使用"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 件のパスキー"</string>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
index 5b8398a..ee94b7e 100644
--- a/packages/CredentialManager/res/values-ka/strings.xml
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"გაუქმება"</string>
<string name="string_continue" msgid="1346732695941131882">"გაგრძელება"</string>
<string name="string_more_options" msgid="7990658711962795124">"სხვა ვარიანტები"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"შეიტყვეთ მეტი"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"უფრო უსაფრთხოა წვდომის გასაღების შემთხვევაში"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"წვდომის გასაღებების დახმარებით აღარ მოგიწევთ რთული პაროლების შექმნა და დამახსოვრება"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"წვდომის გასაღებები დაშიფრული ციფრული გასაღებებია, რომლებსაც თქვენი თითის ანაბეჭდით, სახით ან ეკრანის დაბლოკვით ქმნით"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ისინი შეინახება პაროლების მმართველში, რათა სხვა მოწყობილობებიდან შესვლაც შეძლოთ"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"დამატებითი ინფორმაცია წვდომის გასაღებების შესახებ"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"უპაროლო ტექნოლოგია"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"წვდომის გასაღებები საშუალებას გაძლევთ, სისტემაში შეხვიდეთ პაროლის გარეშე. უბრალოდ, ვინაობის დასადასტურებლად და წვდომის გასაღების შესაქმნელად უნდა გამოიყენოთ თითის ანაბეჭდი, სახით ამოცნობა, PIN-კოდი, ან განბლოკვის გრაფიკული გასაღები."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"საჯარო გასაღების კრიპტოგრაფია"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO-ს ალიანსისა (Google, Apple, Microsoft და ა.შ.) და W3C-ის სტანდარტების შესაბამისად, წვდომის გასაღებები იყენებს კრიპტოგრაფიულ გასაღებების წყვილს. მომხ. სახ. და სიმბ. სტრიქ. განსხვავებით, რომელთაც პაროლ. ვიყენებთ, რამდენიმე პირადი/საჯარო გას. იქმნება აპის ან ვებსაიტისთვის. პირადი გასაღები უსაფრთხოდ ინახება მოწყობილობაზე ან პაროლ. მმართველში და ის ადასტურებს თქვენს ვინაობას. საჯარო გას. ზიარდება აპისა და ვებ. სერვერის მეშვეობით. შესაბ. გასაღებებით შეგიძლიათ მაშინვე დარეგისტ. და სისტ. შეხვიდეთ."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"ანგარიშის გაუმჯობესებული უსაფრთხოება"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"თითოეული გასაღები დაკავშირებულია მხოლოდ აპთან ან ვებსაიტთან, რომელთათვისაც ის შეიქმნა, ამიტომაც შემთხვევით ვერასდროს შეხვალთ თაღლითურ აპში თუ ვებსაიტზე. ამასთანავე, სერვერები ინახავს მხოლოდ საჯარო გასაღებებს, რაც ართულებს გატეხვის ალბათობას."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"დაუბრკოლებელი გადასვლა"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"უპაროლო მომავალში პაროლები კვლავ ხელმისაწვდომი იქნება, წვდომის გასაღებებთან ერთად."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"აირჩიეთ სად შეინახოთ თქვენი <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"აირჩიეთ პაროლების მმართველი თქვენი ინფორმაციის შესანახად, რომ მომავალში უფრო სწრაფად შეხვიდეთ."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"შექმნით წვდომის გასაღებს <xliff:g id="APPNAME">%1$s</xliff:g> აპისთვის?"</string>
diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml
index bf58b21..88c3cb6 100644
--- a/packages/CredentialManager/res/values-kk/strings.xml
+++ b/packages/CredentialManager/res/values-kk/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Бас тарту"</string>
<string name="string_continue" msgid="1346732695941131882">"Жалғастыру"</string>
<string name="string_more_options" msgid="7990658711962795124">"Басқа опциялар"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Кіру кілттерімен қауіпсіздеу"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Кіру кілттері бар кезде күрделі құпия сөздер жасаудың немесе оларды есте сақтаудың қажеті жоқ."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Кіру кілттері — саусақ ізі, бет не экран құлпы арқылы жасалатын шифрланған цифрлық кілттер."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Олар құпия сөз менеджеріне сақталады. Соның арқасында басқа құрылғылардан кіре аласыз."</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> қайда сақталатынын таңдаңыз"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Мәліметіңізді сақтап, келесіде жылдам кіру үшін құпия сөз менеджерін таңдаңыз."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> үшін кіру кілтін жасау керек пе?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> үшін құпия сөзді сақтау керек пе?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> үшін кіру мәліметін сақтау керек пе?"</string>
<string name="passkey" msgid="632353688396759522">"кіру кілті"</string>
<string name="password" msgid="6738570945182936667">"құпия сөз"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"Кіру кілттері"</string>
+ <string name="passwords" msgid="5419394230391253816">"Құпия сөздер"</string>
<string name="sign_ins" msgid="4710739369149469208">"кіру әрекеттері"</string>
<string name="sign_in_info" msgid="2627704710674232328">"кіру мәліметі"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> тіркелу дерегін сақтау орны:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Кіру кілтін басқа құрылғыда жасау керек пе?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Барлық кіру әрекеті үшін <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> пайдаланылсын ба?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Аккаунтқа кіру оңай болуы үшін, құпия сөз менеджері құпия сөздер мен кіру кілттерін сақтайды."</string>
<string name="set_as_default" msgid="4415328591568654603">"Әдепкі етіп орнату"</string>
<string name="use_once" msgid="9027366575315399714">"Бір рет пайдалану"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> құпия сөз • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> кіру кілті"</string>
diff --git a/packages/CredentialManager/res/values-km/strings.xml b/packages/CredentialManager/res/values-km/strings.xml
index 1966aa1..f40ddfb 100644
--- a/packages/CredentialManager/res/values-km/strings.xml
+++ b/packages/CredentialManager/res/values-km/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"បោះបង់"</string>
<string name="string_continue" msgid="1346732695941131882">"បន្ត"</string>
<string name="string_more_options" msgid="7990658711962795124">"ជម្រើសច្រើនទៀត"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"កាន់តែមានសុវត្ថិភាពដោយប្រើកូដសម្ងាត់"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"តាមរយៈកូដសម្ងាត់ អ្នកមិនចាំបាច់បង្កើត ឬចងចាំពាក្យសម្ងាត់ស្មុគស្មាញនោះទេ"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"កូដសម្ងាត់ត្រូវបានអ៊ីនគ្រីបឃីឌីជីថលដែលអ្នកបង្កើតដោយប្រើស្នាមម្រាមដៃ មុខ ឬចាក់សោអេក្រង់របស់អ្នក"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"កូដសម្ងាត់ត្រូវបានរក្សាទុកទៅក្នុងកម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ ដូច្នេះអ្នកអាចចូលនៅលើឧបករណ៍ផ្សេងទៀត"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"ជ្រើសរើសកន្លែងដែលត្រូវរក្សាទុក<xliff:g id="CREATETYPES">%1$s</xliff:g>របស់អ្នក"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"ជ្រើសរើសកម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ ដើម្បីរក្សាទុកព័ត៌មានរបស់អ្នក និងចូលគណនីបានកាន់តែរហ័សនៅពេលលើកក្រោយ"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"បង្កើតកូដសម្ងាត់សម្រាប់ <xliff:g id="APPNAME">%1$s</xliff:g> ឬ?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"រក្សាទុកពាក្យសម្ងាត់សម្រាប់ <xliff:g id="APPNAME">%1$s</xliff:g> ឬ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"រក្សាទុកព័ត៌មានអំពីការចូលគណនីសម្រាប់ <xliff:g id="APPNAME">%1$s</xliff:g> ឬ?"</string>
<string name="passkey" msgid="632353688396759522">"កូដសម្ងាត់"</string>
<string name="password" msgid="6738570945182936667">"ពាក្យសម្ងាត់"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"កូដសម្ងាត់"</string>
+ <string name="passwords" msgid="5419394230391253816">"ពាក្យសម្ងាត់"</string>
<string name="sign_ins" msgid="4710739369149469208">"ការចូលគណនី"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ព័ត៌មានអំពីការចូលគណនី"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"រក្សាទុក <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ទៅកាន់"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"បង្កើតកូដសម្ងាត់នៅក្នុងឧបករណ៍ផ្សេងទៀតឬ?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ប្រើ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> សម្រាប់ការចូលគណនីទាំងអស់របស់អ្នកឬ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់នេះនឹងរក្សាទុកពាក្យសម្ងាត់ និងកូដសម្ងាត់របស់អ្នក ដើម្បីជួយឱ្យអ្នកចូលគណនីបានយ៉ាងងាយស្រួល"</string>
<string name="set_as_default" msgid="4415328591568654603">"កំណត់ជាលំនាំដើម"</string>
<string name="use_once" msgid="9027366575315399714">"ប្រើម្ដង"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"ពាក្យសម្ងាត់ <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • កូដសម្ងាត់<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml
index cfc1098..cbc9a26 100644
--- a/packages/CredentialManager/res/values-kn/strings.xml
+++ b/packages/CredentialManager/res/values-kn/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"ರದ್ದುಗೊಳಿಸಿ"</string>
<string name="string_continue" msgid="1346732695941131882">"ಮುಂದುವರಿಸಿ"</string>
<string name="string_more_options" msgid="7990658711962795124">"ಇನ್ನಷ್ಟು ಆಯ್ಕೆಗಳು"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"ಪಾಸ್ಕೀಗಳೊಂದಿಗೆ ಸುರಕ್ಷಿತವಾಗಿರುತ್ತವೆ"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"ಪಾಸ್ಕೀಗಳ ಮೂಲಕ, ನೀವು ಕ್ಲಿಷ್ಟ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ರಚಿಸುವ ಅಥವಾ ನೆನಪಿಟ್ಟುಕೊಳ್ಳುವ ಅಗತ್ಯವಿಲ್ಲ"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"ಪಾಸ್ಕೀಗಳು ನಿಮ್ಮ ಫಿಂಗರ್ಪ್ರಿಂಟ್, ಫೇಸ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ನೀವು ರಚಿಸುವ ಎನ್ಕ್ರಿಪ್ಟ್ ಮಾಡಿದ ಡಿಜಿಟಲ್ ಕೀಗಳಾಗಿವೆ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ಅವುಗಳನ್ನು ಪಾಸ್ವರ್ಡ್ ನಿರ್ವಾಹಕದಲ್ಲಿ ಉಳಿಸಲಾಗಿದೆ, ಹಾಗಾಗಿ ನೀವು ಇತರ ಸಾಧನಗಳಲ್ಲಿ ಸೈನ್ ಇನ್ ಮಾಡಬಹುದು"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"ಪಾಸ್ಕೀಗಳ ಕುರಿತು ಇನ್ನಷ್ಟು"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"ಪಾಸ್ವರ್ಡ್ ರಹಿತ ತಂತ್ರಜ್ಞಾನ"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"ಪಾಸ್ಕೀಗಳು ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಅವಲಂಬಿಸದೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ಅನುಮತಿಸುತ್ತದೆ. ನಿಮ್ಮ ಗುರುತನ್ನು ಪರಿಶೀಲಿಸಲು ಮತ್ತು ಪಾಸ್ಕೀ ರಚಿಸಲು ನಿಮ್ಮ ಫಿಂಗರ್ಪ್ರಿಂಟ್, ಮುಖ ಗುರುತಿಸುವಿಕೆ, ಪಿನ್ ಅಥವಾ ಸ್ವೈಪ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು ಬಳಸಬೇಕಾಗುತ್ತದೆ."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"ಸಾರ್ವಜನಿಕ ಕೀ ಕ್ರಿಪ್ಟೋಗ್ರಫಿ"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO ಅಲೈಯನ್ಸ್ (ಇದು Google, Apple, Microsoft ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ) ಮತ್ತು W3C ಮಾನದಂಡಗಳನ್ನು ಆಧರಿಸಿ, ಪಾಸ್ಕೀಗಳು ಕ್ರಿಪ್ಟೋಗ್ರಾಫಿಕ್ ಕೀ ಜೋಡಿಗಳನ್ನು ಬಳಸುತ್ತವೆ. ಪಾಸ್ವರ್ಡ್ಗಳಿಗಾಗಿ ನಾವು ಬಳಸುವ ಬಳಕೆದಾರಹೆಸರು ಮತ್ತು ಅಕ್ಷರಗಳ ಸ್ಟ್ರಿಂಗ್ಗಿಂತ ಭಿನ್ನವಾಗಿ, ಆ್ಯಪ್ ಅಥವಾ ವೆಬ್ಸೈಟ್ಗಾಗಿ ಖಾಸಗಿ-ಸಾರ್ವಜನಿಕ ಕೀ ಜೋಡಿಯನ್ನು ರಚಿಸಲಾಗಿದೆ. ಖಾಸಗಿ ಕೀ ಅನ್ನು ನಿಮ್ಮ ಸಾಧನ ಅಥವಾ ಪಾಸ್ವರ್ಡ್ ನಿರ್ವಾಹಕದಲ್ಲಿ ಸುರಕ್ಷಿತವಾಗಿ ಸಂಗ್ರಹಿಸಲಾಗಿದೆ ಮತ್ತು ಅದು ನಿಮ್ಮ ಗುರುತನ್ನು ಖಚಿತಪಡಿಸುತ್ತದೆ. ಸಾರ್ವಜನಿಕ ಕೀ ಅನ್ನು ಆ್ಯಪ್ ಅಥವಾ ವೆಬ್ಸೈಟ್ ಸರ್ವರ್ ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ. ಅನುಗುಣವಾದ ಕೀ ಮೂಲಕ, ನೀವು ತಕ್ಷಣ ನೋಂದಾಯಿಸಬಹುದು ಮತ್ತು ಸೈನ್ ಇನ್ ಮಾಡಬಹುದು."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"ಸುಧಾರಿತ ಖಾತೆಯ ಭದ್ರತೆ"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"ಪ್ರತಿಯೊಂದು ಕೀ ಅವುಗಳನ್ನು ರಚಿಸಲಾದ ಆ್ಯಪ್ ಅಥವಾ ವೆಬ್ಸೈಟ್ನ ಜೊತೆಗೆ ಪ್ರತ್ಯೇಕವಾಗಿ ಲಿಂಕ್ ಮಾಡಲಾಗಿದೆ, ಆದ್ದರಿಂದ ನೀವು ಎಂದಿಗೂ ತಪ್ಪಾಗಿ ವಂಚನೆಯ ಆ್ಯಪ್ ಅಥವಾ ವೆಬ್ಸೈಟ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಜೊತೆಗೆ, ಸರ್ವರ್ಗಳು ಮಾತ್ರ ಸಾರ್ವಜನಿಕ ಕೀಗಳನ್ನು ಇಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ, ಹ್ಯಾಕಿಂಗ್ ಮಾಡುವುದು ತುಂಬಾ ಕಷ್ಟಕರವಾಗಿದೆ."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"ಅಡಚಣೆರಹಿತ ಪರಿವರ್ತನೆ"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"ನಾವು ಪಾಸ್ವರ್ಡ್ ರಹಿತ ತಂತ್ರಜ್ಞಾನದ ಕಡೆಗೆ ಸಾಗುತ್ತಿರುವಾಗ, ಪಾಸ್ಕೀಗಳ ಜೊತೆಗೆ ಪಾಸ್ವರ್ಡ್ಗಳು ಇನ್ನೂ ಲಭ್ಯವಿರುತ್ತವೆ."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"ನಿಮ್ಮ <xliff:g id="CREATETYPES">%1$s</xliff:g> ಅನ್ನು ಎಲ್ಲಿ ಉಳಿಸಬೇಕು ಎಂದು ಆರಿಸಿ"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಉಳಿಸಲು ಪಾಸ್ವರ್ಡ್ ನಿರ್ವಾಹಕವನ್ನು ಆಯ್ಕೆಮಾಡಿ ಹಾಗೂ ಮುಂದಿನ ಬಾರಿ ವೇಗವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್ಕೀ ಅನ್ನು ರಚಿಸುವುದೇ?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್ವರ್ಡ್ ಉಳಿಸುವುದೇ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> ಗಾಗಿ ಸೈನ್-ಇನ್ ಮಾಹಿತಿಯನ್ನು ಉಳಿಸುವುದೇ?"</string>
<string name="passkey" msgid="632353688396759522">"ಪಾಸ್ಕೀ"</string>
<string name="password" msgid="6738570945182936667">"ಪಾಸ್ವರ್ಡ್"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ಪಾಸ್ಕೀಗಳು"</string>
+ <string name="passwords" msgid="5419394230391253816">"ಪಾಸ್ವರ್ಡ್ಗಳು"</string>
<string name="sign_ins" msgid="4710739369149469208">"ಸೈನ್-ಇನ್ಗಳು"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ಸೈನ್-ಇನ್ ಮಾಹಿತಿ"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"ಇಲ್ಲಿಗೆ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ಅನ್ನು ಉಳಿಸಿ"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"ಮತ್ತೊಂದು ಸಾಧನದಲ್ಲಿ ಪಾಸ್ಕೀಯನ್ನು ರಚಿಸಬೇಕೇ?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ನಿಮ್ಮ ಎಲ್ಲಾ ಸೈನ್-ಇನ್ಗಳಿಗಾಗಿ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸುವುದೇ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"ಈ ಪಾಸ್ವರ್ಡ್ ನಿರ್ವಾಹಕವು ನಿಮಗೆ ಸುಲಭವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡುವುದಕ್ಕೆ ಸಹಾಯ ಮಾಡಲು ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಪಾಸ್ಕೀಗಳನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ"</string>
<string name="set_as_default" msgid="4415328591568654603">"ಡೀಫಾಲ್ಟ್ ಆಗಿ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="use_once" msgid="9027366575315399714">"ಒಂದು ಬಾರಿ ಬಳಸಿ"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ಪಾಸ್ವರ್ಡ್ಗಳು • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ಪಾಸ್ಕೀಗಳು"</string>
diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml
index ed53a6c..beef76f 100644
--- a/packages/CredentialManager/res/values-ko/strings.xml
+++ b/packages/CredentialManager/res/values-ko/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"취소"</string>
<string name="string_continue" msgid="1346732695941131882">"계속"</string>
<string name="string_more_options" msgid="7990658711962795124">"옵션 더보기"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"패스키로 더 안전하게"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"패스키를 사용하면 복잡한 비밀번호를 만들거나 기억하지 않아도 됩니다."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"패스키는 지문, 얼굴 또는 화면 잠금으로 생성하는 암호화된 디지털 키입니다."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"비밀번호 관리자에 저장되므로 다른 기기에서 로그인할 수 있습니다."</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> 저장 위치 선택"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"정보를 저장해서 다음에 더 빠르게 로그인하려면 비밀번호 관리자를 선택하세요."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>의 패스키를 만드시겠습니까?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g>의 비밀번호를 저장하시겠습니까?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g>의 로그인 정보를 저장하시겠습니까?"</string>
<string name="passkey" msgid="632353688396759522">"패스키"</string>
<string name="password" msgid="6738570945182936667">"비밀번호"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"패스키"</string>
+ <string name="passwords" msgid="5419394230391253816">"비밀번호"</string>
<string name="sign_ins" msgid="4710739369149469208">"로그인 정보"</string>
<string name="sign_in_info" msgid="2627704710674232328">"로그인 정보"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> 저장 위치"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"다른 기기에서 패스키를 만드시겠습니까?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"모든 로그인에 <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>을(를) 사용하시겠습니까?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"이 비밀번호 관리자는 비밀번호와 패스키를 저장하여 사용자가 간편하게 로그인할 수 있도록 돕습니다."</string>
<string name="set_as_default" msgid="4415328591568654603">"기본값으로 설정"</string>
<string name="use_once" msgid="9027366575315399714">"한 번 사용"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"비밀번호 <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>개 • 패스키 <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>개"</string>
diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml
index 68ce3c7..6afbb08 100644
--- a/packages/CredentialManager/res/values-ky/strings.xml
+++ b/packages/CredentialManager/res/values-ky/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Жок"</string>
<string name="string_continue" msgid="1346732695941131882">"Улантуу"</string>
<string name="string_more_options" msgid="7990658711962795124">"Башка варианттар"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Мүмкүндүк алуу ачкычтары менен коопсузураак болот"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Мүмкүндүк алуу ачкычтары менен татаал сырсөздөрдү түзүп же эстеп калуунун кереги жок"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Мүмкүндүк алуу ачкычтары – манжаңыздын изи, жүзүңүз же экранды кулпулоо функциясы аркылуу түзгөн шифрленген санариптик ачкычтар"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Алар сырсөздөрдү башкаргычка сакталып, аккаунтуңузга башка түзмөктөрдөн кире аласыз"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> кайда сакталарын тандаңыз"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Маалыматыңызды сактоо жана кийинки жолу тезирээк кирүү үчүн сырсөздөрдү башкаргычты тандаңыз"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> үчүн мүмкүндүк алуу ачкычын түзөсүзбү?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> үчүн сырсөз сакталсынбы?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> үчүн кирүү маалыматы сакталсынбы?"</string>
<string name="passkey" msgid="632353688396759522">"мүмкүндүк алуу ачкычы"</string>
<string name="password" msgid="6738570945182936667">"сырсөз"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"мүмкүндүк алуу ачкычтары"</string>
+ <string name="passwords" msgid="5419394230391253816">"сырсөздөр"</string>
<string name="sign_ins" msgid="4710739369149469208">"кирүүлөр"</string>
<string name="sign_in_info" msgid="2627704710674232328">"кирүү маалыматы"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> төмөнкүгө сакталсын:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Мүмкүндүк алуу ачкычы башка түзмөктө түзүлсүнбү?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> бардык аккаунттарга кирүү үчүн колдонулсунбу?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Сырсөздөрүңүздү жана ачкычтарыңызды Сырсөздөрдү башкаргычка сактап коюп, каалаган убакта колдоно берсеңиз болот"</string>
<string name="set_as_default" msgid="4415328591568654603">"Демейки катары коюу"</string>
<string name="use_once" msgid="9027366575315399714">"Бир жолу колдонуу"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> сырсөз • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> мүмкүндүк алуу ачкычы"</string>
diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml
index 9814521..b061db4 100644
--- a/packages/CredentialManager/res/values-lo/strings.xml
+++ b/packages/CredentialManager/res/values-lo/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"ຍົກເລີກ"</string>
<string name="string_continue" msgid="1346732695941131882">"ສືບຕໍ່"</string>
<string name="string_more_options" msgid="7990658711962795124">"ຕົວເລືອກເພີ່ມເຕີມ"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"ປອດໄພຂຶ້ນດ້ວຍກະແຈຜ່ານ"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"ໂດຍການໃຊ້ກະແຈຜ່ານ, ທ່ານບໍ່ຈຳເປັນຕ້ອງສ້າງ ຫຼື ຈື່ລະຫັດຜ່ານທີ່ຊັບຊ້ອນ"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"ກະແຈຜ່ານແມ່ນກະແຈດິຈິຕອນທີ່ໄດ້ຖືກເຂົ້າລະຫັດໄວ້ເຊິ່ງທ່ານສ້າງຂຶ້ນໂດຍໃຊ້ລາຍນິ້ວມື, ໃບໜ້າ ຫຼື ການລັອກໜ້າຈໍຂອງທ່ານ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ພວກມັນຖືກບັນທຶກໄວ້ຢູ່ໃນຕົວຈັດການລະຫັດຜ່ານ, ດັ່ງນັ້ນທ່ານສາມາດເຂົ້າສູ່ລະບົບຢູ່ອຸປະກອນອື່ນໆໄດ້"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"ເລືອກບ່ອນທີ່ຈະບັນທຶກ <xliff:g id="CREATETYPES">%1$s</xliff:g> ຂອງທ່ານ"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"ເລືອກຕົວຈັດການລະຫັດຜ່ານເພື່ອບັນທຶກຂໍ້ມູນຂອງທ່ານ ແລະ ເຂົ້າສູ່ລະບົບໄວຂຶ້ນໃນເທື່ອຕໍ່ໄປ"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ສ້າງກະແຈຜ່ານສຳລັບ <xliff:g id="APPNAME">%1$s</xliff:g> ບໍ?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"ບັນທຶກລະຫັດຜ່ານສຳລັບ <xliff:g id="APPNAME">%1$s</xliff:g> ບໍ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"ບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບສຳລັບ <xliff:g id="APPNAME">%1$s</xliff:g> ບໍ?"</string>
<string name="passkey" msgid="632353688396759522">"ກະແຈຜ່ານ"</string>
<string name="password" msgid="6738570945182936667">"ລະຫັດຜ່ານ"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ກະແຈຜ່ານ"</string>
+ <string name="passwords" msgid="5419394230391253816">"ລະຫັດຜ່ານ"</string>
<string name="sign_ins" msgid="4710739369149469208">"ການເຂົ້າສູ່ລະບົບ"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ຂໍ້ມູນການເຂົ້າສູ່ລະບົບ"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"ບັນທຶກ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ໃສ່"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"ສ້າງກະແຈຜ່ານໃນອຸປະກອນອື່ນບໍ?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ໃຊ້ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ສຳລັບການເຂົ້າສູ່ລະບົບທັງໝົດຂອງທ່ານບໍ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"ຕົວຈັດການລະຫັດຜ່ານນີ້ຈະຈັດເກັບລະຫັດຜ່ານ ແລະ ກະແຈຜ່ານຂອງທ່ານໄວ້ເພື່ອຊ່ວຍໃຫ້ທ່ານເຂົ້າສູ່ລະບົບໄດ້ໂດຍງ່າຍ"</string>
<string name="set_as_default" msgid="4415328591568654603">"ຕັ້ງເປັນຄ່າເລີ່ມຕົ້ນ"</string>
<string name="use_once" msgid="9027366575315399714">"ໃຊ້ເທື່ອດຽວ"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ລະຫັດຜ່ານ • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ກະແຈຜ່ານ"</string>
diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml
index 5ec68bb..4197e14 100644
--- a/packages/CredentialManager/res/values-lt/strings.xml
+++ b/packages/CredentialManager/res/values-lt/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"Atšaukti"</string>
<string name="string_continue" msgid="1346732695941131882">"Tęsti"</string>
<string name="string_more_options" msgid="7990658711962795124">"Daugiau parinkčių"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Sužinokite daugiau"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Saugiau naudojant slaptažodžius"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Naudojant „passkey“ nereikės kurti ir prisiminti sudėtingų slaptažodžių"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"„Passkey“ šifruojami skaitiniais raktais, kuriuos sukuriate naudodami piršto atspaudą, veidą ar ekrano užraktą"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Jie saugomi slaptažodžių tvarkyklėje, kad galėtumėte prisijungti kituose įrenginiuose"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Daugiau apie slaptuosius raktus („passkey“)"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Technologijos be slaptažodžių"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Naudodami slaptuosius raktus galite prisijungti be slaptažodžių. Tiesiog naudokite piršto atspaudą, atpažinimą pagal veidą, PIN kodą arba perbraukiamą atrakinimo piešinį, kad patvirtintumėte tapatybę ir sukurtumėte slaptąjį raktą."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Viešojo rakto kriptografija"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Pagal FIDO aljansą (kuriam priklauso „Google“, „Apple“, „Microsoft“ ir kt.) bei W3C standartus, slaptiesiems raktams naudojamos kriptografinių raktų poros. Priešingai nei naudotojo vardas ir eilutė simbolių, naudojamų slaptažodžiams, privataus ir viešojo raktų pora sukuriama programai ar svetainei. Tapatybę patvirtinantis privatusis raktas saugomas įrenginyje ar Slaptažodžių tvarkyklėje. Viešasis raktas bendrinamas su programos ar svetainės serveriu. Naudodami atitinkamus raktus galite akimirksniu užsiregistruoti ir prisijungti."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Geresnė paskyros sauga"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Kiekvienas raktas išskirtinai susietas su programa ar svetaine, kuriai buvo sukurtas, todėl niekada per klaidą neprisijungsite prie apgavikiškos programos ar svetainės. Be to, viešieji raktai laikomi tik serveriuose, todėl įsilaužti tampa gerokai sudėtingiau."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Sklandus perėjimas"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Kol stengiamės padaryti, kad ateityje nereikėtų naudoti slaptažodžių, jie vis dar bus pasiekiami kartu su slaptaisiais raktais."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"Pasirinkite, kur išsaugoti „<xliff:g id="CREATETYPES">%1$s</xliff:g>“"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Pasirinkite slaptažodžių tvarkyklę, kurią naudodami galėsite išsaugoti informaciją ir kitą kartą prisijungti greičiau"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Sukurti „passkey“, skirtą „<xliff:g id="APPNAME">%1$s</xliff:g>“?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Išsaugoti „<xliff:g id="APPNAME">%1$s</xliff:g>“ slaptažodį?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Išsaugoti prisijungimo prie „<xliff:g id="APPNAME">%1$s</xliff:g>“ informaciją?"</string>
<string name="passkey" msgid="632353688396759522">"„passkey“"</string>
<string name="password" msgid="6738570945182936667">"slaptažodis"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"passkey"</string>
+ <string name="passwords" msgid="5419394230391253816">"slaptažodžiai"</string>
<string name="sign_ins" msgid="4710739369149469208">"prisijungimo informacija"</string>
<string name="sign_in_info" msgid="2627704710674232328">"prisijungimo informaciją"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Išsaugoti <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Kurti „passkey“ kitame įrenginyje?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Naudoti <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> visada prisijungiant?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Šioje slaptažodžių tvarkyklėje bus saugomi jūsų slaptažodžiai ir „passkey“, kad galėtumėte lengvai prisijungti"</string>
<string name="set_as_default" msgid="4415328591568654603">"Nustatyti kaip numatytąjį"</string>
<string name="use_once" msgid="9027366575315399714">"Naudoti vieną kartą"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Slaptažodžių: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • „Passkey“: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml
index 71ab864..1ba1ef1 100644
--- a/packages/CredentialManager/res/values-lv/strings.xml
+++ b/packages/CredentialManager/res/values-lv/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Atcelt"</string>
<string name="string_continue" msgid="1346732695941131882">"Turpināt"</string>
<string name="string_more_options" msgid="7990658711962795124">"Citas opcijas"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Lielāka drošība ar piekļuves atslēgām"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Izmantojot piekļuves atslēgas, nav jāveido vai jāatceras sarežģītas paroles."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Piekļuves atslēgas ir šifrētas digitālas atslēgas, ko varat izveidot, izmantojot pirksta nospiedumu, seju vai ekrāna bloķēšanas informāciju."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Tās tiek saglabātas paroļu pārvaldniekā, lai jūs varētu pierakstīties citās ierīcēs."</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Izvēlieties, kur saglabāt savas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Lai saglabātu informāciju un nākamreiz varētu pierakstīties ātrāk, atlasiet paroļu pārvaldnieku."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vai izveidot piekļuves atslēgu lietotnei <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Vai saglabāt paroli lietotnei <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vai saglabāt pierakstīšanās informāciju lietotnei <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"piekļuves atslēga"</string>
<string name="password" msgid="6738570945182936667">"parole"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"piekļuves atslēgas"</string>
+ <string name="passwords" msgid="5419394230391253816">"paroles"</string>
<string name="sign_ins" msgid="4710739369149469208">"pierakstīšanās informācija"</string>
<string name="sign_in_info" msgid="2627704710674232328">"pierakstīšanās informācija"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Kur jāsaglabā <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Vai izveidot piekļuves atslēgu citā ierīcē?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Vai vienmēr izmantot <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>, lai pierakstītos?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Šis paroļu pārvaldnieks glabās jūsu paroles un piekļuves atslēgas, lai atvieglotu pierakstīšanos."</string>
<string name="set_as_default" msgid="4415328591568654603">"Iestatīt kā noklusējumu"</string>
<string name="use_once" msgid="9027366575315399714">"Izmantot vienreiz"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Paroļu skaits: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Piekļuves atslēgu skaits: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml
index ae7473f..1be7ca5 100644
--- a/packages/CredentialManager/res/values-mk/strings.xml
+++ b/packages/CredentialManager/res/values-mk/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Откажи"</string>
<string name="string_continue" msgid="1346732695941131882">"Продолжи"</string>
<string name="string_more_options" msgid="7990658711962795124">"Повеќе опции"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Побезбедно со криптографски клучеви"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Со криптографските клучеви нема потреба да создавате или да помните сложени лозинки"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Криптографските клучеви се шифрирани дигитални клучеви што ги создавате со вашиот отпечаток, лик или заклучување екран"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Се зачувуваат во управник со лозинки за да може да се најавувате на други уреди"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"Изберете каде да ги зачувате вашите <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Изберете Password Manager за да ги зачувате вашите податоци и да се најавите побрзо следниот пат"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Да се создаде криптографски клуч за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-ml/strings.xml b/packages/CredentialManager/res/values-ml/strings.xml
index 4075e69..aeef052 100644
--- a/packages/CredentialManager/res/values-ml/strings.xml
+++ b/packages/CredentialManager/res/values-ml/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"റദ്ദാക്കുക"</string>
<string name="string_continue" msgid="1346732695941131882">"തുടരുക"</string>
<string name="string_more_options" msgid="7990658711962795124">"കൂടുതൽ ഓപ്ഷനുകൾ"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"കൂടുതലറിയുക"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"പാസ്കീകൾ ഉപയോഗിച്ച് സുരക്ഷിതരാകൂ"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"പാസ്കീകൾ ഉപയോഗിക്കുമ്പോൾ നിങ്ങൾ സങ്കീർണ്ണമായ പാസ്വേഡുകൾ സൃഷ്ടിക്കുകയോ ഓർമ്മിക്കുകയോ ചെയ്യേണ്ടതില്ല"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"ഫിംഗർപ്രിന്റ്, മുഖം അല്ലെങ്കിൽ സ്ക്രീൻ ലോക്ക് ഉപയോഗിച്ച് നിങ്ങൾ സൃഷ്ടിക്കുന്ന എൻക്രിപ്റ്റ് ചെയ്ത ഡിജിറ്റൽ കീകളാണ് പാസ്കീകൾ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"അവ ഒരു പാസ്വേഡ് മാനേജറിൽ സംരക്ഷിക്കുന്നതിനാൽ നിങ്ങൾക്ക് മറ്റ് ഉപകരണങ്ങളിലും സൈൻ ഇൻ ചെയ്യാം"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"പാസ്കീകളെ കുറിച്ച് കൂടുതൽ വിവരങ്ങൾ"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"പാസ്വേഡ് രഹിത സാങ്കേതികവിദ്യ"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"പാസ്വേഡുകളെ ആശ്രയിക്കാതെ സൈൻ ഇൻ ചെയ്യാൻ പാസ്കീകൾ അനുവദിക്കുന്നു. നിങ്ങളുടെ ഐഡന്റിറ്റി പരിശോധിച്ചുറപ്പിച്ച് പാസ്കീ സൃഷ്ടിക്കാൻ നിങ്ങളുടെ ഫിംഗർപ്രിന്റ്, മുഖം തിരിച്ചറിയൽ, പിൻ അല്ലെങ്കിൽ സ്വൈപ്പ് പാറ്റേൺ മാത്രം ഉപയോഗിച്ചാൽ മതി."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"എല്ലാവർക്കുമായുള്ള കീ ക്രിപ്റ്റോഗ്രഫി"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO Alliance (Google, Apple, Microsoft എന്നിവയും മറ്റും ഉൾപ്പെടുന്നത്), W3C മാനദണ്ഡ പ്രകാരം, പാസ്കീ, ക്രിപ്റ്റോഗ്രാഫിക് കീ ജോടി ഉപയോഗിക്കുന്നു. പാസ്വേഡിലുള്ള ഉപയോക്തൃനാമം, പ്രതീകം ഇവയ്ക്ക് പകരം, ആപ്പിനോ വെബ്സൈറ്റിനോ വേണ്ടി പ്രൈവറ്റ്-പബ്ലിക് കീ ജോടി സൃഷ്ടിക്കുന്നു. സ്വകാര്യ കീ നിങ്ങളുടെ ഉപകരണത്തിലോ Password Manager-ലോ സംഭരിക്കുന്നു, ഇത് നിങ്ങളുടെ ഐഡന്റിറ്റി സ്ഥിരീകരിക്കുന്നു. എല്ലാവർക്കുമായുള്ള കീ ആപ്പ്/വെബ്സൈറ്റുമായി പങ്കിടുന്നു. അനുബന്ധ കീ ഉപയോഗിച്ച്, ഉടൻ രജിസ്റ്റർ ചെയ്ത് സൈൻ ഇൻ ചെയ്യാം."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"മെച്ചപ്പെടുത്തിയ അക്കൗണ്ട് സുരക്ഷ"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"ഓരോ കീയും ഏത് ആപ്പിന് അല്ലെങ്കിൽ വെബ്സൈറ്റിന് വേണ്ടിയാണോ സൃഷ്ടിച്ചത്, അതുമായി മാത്രം ലിങ്ക് ചെയ്തിരിക്കുന്നു, അതുകൊണ്ട് നിങ്ങൾ ഒരിക്കലും വഞ്ചനാപരമായ ഒരു ആപ്പിലേക്കോ വെബ്സൈറ്റിലേക്കോ അബദ്ധവശാൽ സൈൻ ഇൻ ചെയ്യില്ല. ഇതോടൊപ്പം, സെർവറുകളിൽ എല്ലാവർക്കുമായുള്ള കീകൾ മാത്രം സൂക്ഷിക്കുന്നതിനാൽ ഹാക്ക് ചെയ്യാൻ വളരെ ബുദ്ധിമുട്ടാണ്."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"ആയാസരഹിതമായ മാറ്റം"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"നമ്മൾ പാസ്വേഡ് രഹിത ഭാവിയിലേക്ക് ചുവടുവെച്ചുകൊണ്ടിരിക്കുകയാണ് എങ്കിലും, പാസ്കീകൾക്കൊപ്പം പാസ്വേഡുകൾ തുടർന്നും ലഭ്യമായിരിക്കും."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"നിങ്ങളുടെ <xliff:g id="CREATETYPES">%1$s</xliff:g> എവിടെയാണ് സംരക്ഷിക്കേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"നിങ്ങളുടെ വിവരങ്ങൾ സംരക്ഷിക്കാനും അടുത്ത തവണ വേഗത്തിൽ സൈൻ ഇൻ ചെയ്യാനും ഒരു പാസ്വേഡ് മാനേജർ തിരഞ്ഞെടുക്കുക"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> എന്നതിനായി പാസ്കീ സൃഷ്ടിക്കണോ?"</string>
diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml
index 0ac9ce8..309a10b 100644
--- a/packages/CredentialManager/res/values-mn/strings.xml
+++ b/packages/CredentialManager/res/values-mn/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Цуцлах"</string>
<string name="string_continue" msgid="1346732695941131882">"Үргэлжлүүлэх"</string>
<string name="string_more_options" msgid="7990658711962795124">"Бусад сонголт"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Passkey-тэй байхад илүү аюулгүй"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Passkey-н тусламжтай та нарийн төвөгтэй нууц үг үүсгэх эсвэл санах шаардлагагүй"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Passkey нь таны хурууны хээ, царай эсвэл дэлгэцийн түгжээгээ ашиглан үүсгэсэн шифрлэгдсэн дижитал түлхүүр юм"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Тэдгээрийг нууц үгний менежерт хадгалдаг бөгөөд ингэснээр та бусад төхөөрөмжид нэвтрэх боломжтой"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g>-г хаана хадгалахаа сонгоно уу"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Мэдээллээ хадгалж, дараагийн удаа илүү хурдан нэвтрэхийн тулд нууц үгний менежерийг сонгоно уу"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>-д passkey үүсгэх үү?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g>-н нууц үгийг хадгалах уу?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g>-н нэвтрэх мэдээллийг хадгалах уу?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"нууц үг"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"passkeys"</string>
+ <string name="passwords" msgid="5419394230391253816">"нууц үг"</string>
<string name="sign_ins" msgid="4710739369149469208">"нэвтрэлт"</string>
<string name="sign_in_info" msgid="2627704710674232328">"нэвтрэх мэдээлэл"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>-г дараахад хадгалах"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Өөр төхөөрөмжид passkey үүсгэх үү?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-г бүх нэвтрэлтдээ ашиглах уу?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Хялбархан нэвтрэхэд туслахын тулд энэ нууц үгний менежер таны нууц үг болон passkeys-г хадгална"</string>
<string name="set_as_default" msgid="4415328591568654603">"Өгөгдмөлөөр тохируулах"</string>
<string name="use_once" msgid="9027366575315399714">"Нэг удаа ашиглах"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> нууц үг • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkey"</string>
diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml
index ab69307..fffcf00 100644
--- a/packages/CredentialManager/res/values-mr/strings.xml
+++ b/packages/CredentialManager/res/values-mr/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"रद्द करा"</string>
<string name="string_continue" msgid="1346732695941131882">"पुढे सुरू ठेवा"</string>
<string name="string_more_options" msgid="7990658711962795124">"आणखी पर्याय"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"पासकीसह आणखी सुरक्षित"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"पासकीसोबत, तुम्हाला क्लिष्ट पासवर्ड तयार करण्याची किंवा लक्षात ठेवण्याची आवश्यकता नाही"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"पासकी या तुम्ही तुमचे फिंगरप्रिंट, फेस किंवा स्क्रीन लॉक वापरून तयार करता अशा एंक्रिप्ट केलेल्या डिजिटल की आहेत"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"त्या Password Manager मध्ये सेव्ह केलेल्या असतात, जेणेकरून तुम्ही इतर डिव्हाइसवर साइन इन करू शकाल"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"तुमची <xliff:g id="CREATETYPES">%1$s</xliff:g> कुठे सेव्ह करायची ते निवडा"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"तुमची माहिती सेव्ह करण्यासाठी आणि पुढच्या वेळी जलद साइन इन करण्याकरिता Password Manager निवडा"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> साठी पासकी तयार करायची का?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> साठी पासवर्ड सेव्ह करायचा का?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> साठी साइन-इन माहिती सेव्ह करायची का?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"पासकी"</string>
+ <string name="passwords" msgid="5419394230391253816">"पासवर्ड"</string>
<string name="sign_ins" msgid="4710739369149469208">"साइन-इन"</string>
<string name="sign_in_info" msgid="2627704710674232328">"साइन-इनसंबंधित माहिती"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> येथे सेव्ह करा"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"दुसऱ्या डिव्हाइसमध्ये पासकी तयार करायची का?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"तुमच्या सर्व साइन-इन साठी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>वापरायचे का?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"तुम्हाला सहजरीत्या साइन इन करण्यात मदत करण्यासाठी हा पासवर्ड व्यवस्थापक तुमचे पासवर्ड आणि पासकी स्टोअर करेल"</string>
<string name="set_as_default" msgid="4415328591568654603">"डिफॉल्ट म्हणून सेट करा"</string>
<string name="use_once" msgid="9027366575315399714">"एकदा वापरा"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> पासकी"</string>
diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml
index d95c3ea..dcc1987 100644
--- a/packages/CredentialManager/res/values-ms/strings.xml
+++ b/packages/CredentialManager/res/values-ms/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"Batal"</string>
<string name="string_continue" msgid="1346732695941131882">"Teruskan"</string>
<string name="string_more_options" msgid="7990658711962795124">"Lagi pilihan"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Ketahui lebih lanjut"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Lebih selamat dengan kunci laluan"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Anda tidak perlu mencipta atau mengingati kata laluan yang rumit dengan kunci laluan"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Kunci laluan ialah kunci digital disulitkan yang anda cipta menggunakan cap jari, wajah atau kunci skrin anda"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Kunci laluan disimpan pada password manager supaya anda boleh log masuk pada peranti lain"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Lagi tentang kunci laluan"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Teknologi tanpa kata laluan"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Kunci laluan membolehkan anda log masuk tanpa bergantung pada kata laluan. Anda hanya perlu menggunakan cap jari anda, pengecaman wajah, PIN atau corak leret untuk mengesahkan identiti anda dan mencipta kunci laluan."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Kriptografi kunci awam"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Berdasarkan standard Perikatan FIDO (termasuk Google, Apple, Microsoft dll) & W3C, kunci laluan menggunakan pasangan kunci kriptografi. Tidak seperti nama pengguna & rentetan aksara yang digunakan untuk kata laluan, pasangan kunci peribadi-umum dicipta untuk apl/laman web. Kunci persendirian akan disimpan dengan selamat pada peranti atau pengurus kata laluan dan ia mengesahkan identiti anda. Kunci awam dikongsi dengan pelayan apl/laman web. Dengan kunci sepadan, anda boleh mendaftar dan log masuk dengan segera."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Keselamatan akaun yang dipertingkatkan"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Setiap kunci dipautkan secara eksklusif dengan apl atau laman web kunci dicipta, jadi anda tidak boleh log masuk ke apl atau laman web penipuan secara tidak sengaja. Selain itu, dengan pelayan yang hanya menyimpan kunci awam, penggodaman menjadi jauh lebih sukar."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Peralihan yang lancar"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Semasa kita bergerak menuju ke arah masa depan tanpa kata laluan, kata laluan masih akan tersedia bersama dengan kunci laluan."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"Pilih tempat untuk menyimpan <xliff:g id="CREATETYPES">%1$s</xliff:g> anda"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Pilih Password Manager untuk menyimpan maklumat anda dan log masuk lebih pantas pada kali seterusnya"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Cipta kunci laluan untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Simpan kata laluan untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Simpan maklumat log masuk untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"kunci laluan"</string>
<string name="password" msgid="6738570945182936667">"kata laluan"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"kunci laluan"</string>
+ <string name="passwords" msgid="5419394230391253816">"kata laluan"</string>
<string name="sign_ins" msgid="4710739369149469208">"log masuk"</string>
<string name="sign_in_info" msgid="2627704710674232328">"maklumat log masuk"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Simpan <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> pada"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Cipta kunci laluan dalam peranti lain?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Gunakan <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> untuk semua log masuk anda?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Password Manager ini akan menyimpan kata laluan dan kunci laluan anda untuk membantu anda log masuk dengan mudah"</string>
<string name="set_as_default" msgid="4415328591568654603">"Tetapkan sebagai lalai"</string>
<string name="use_once" msgid="9027366575315399714">"Gunakan sekali"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> kata laluan • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> kunci laluan"</string>
diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml
index 6630ffa..d4afb28 100644
--- a/packages/CredentialManager/res/values-my/strings.xml
+++ b/packages/CredentialManager/res/values-my/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"မလုပ်တော့"</string>
<string name="string_continue" msgid="1346732695941131882">"ရှေ့ဆက်ရန်"</string>
<string name="string_more_options" msgid="7990658711962795124">"နောက်ထပ်ရွေးစရာများ"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"လျှို့ဝှက်ကီးများဖြင့် ပိုလုံခြုံသည်"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"လျှို့ဝှက်ကီးများဖြင့် ရှုပ်ထွေးသောစကားဝှက်များကို ပြုလုပ်ရန် (သို့) မှတ်မိရန် မလိုပါ"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"လျှို့ဝှက်ကီးများမှာ သင်၏လက်ဗွေ၊ မျက်နှာ (သို့) ဖန်သားပြင်လော့ခ်သုံး၍ ပြုလုပ်ထားသော အသွင်ဝှက်ထားသည့် ဒစ်ဂျစ်တယ်ကီးများ ဖြစ်သည်"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"၎င်းတို့ကို စကားဝှက်မန်နေဂျာတွင် သိမ်းသဖြင့် အခြားစက်များတွင် လက်မှတ်ထိုးဝင်နိုင်ပါသည်"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"သင်၏ <xliff:g id="CREATETYPES">%1$s</xliff:g> သိမ်းရန်နေရာ ရွေးခြင်း"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"သင့်အချက်အလက်သိမ်းပြီး နောက်တစ်ကြိမ်၌ ပိုမိုမြန်ဆန်စွာ လက်မှတ်ထိုးဝင်ရန် စကားဝှက်မန်နေဂျာကို ရွေးပါ"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> အတွက် လျှို့ဝှက်ကီးပြုလုပ်မလား။"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> အတွက် စကားဝှက်ကို သိမ်းမလား။"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> အတွက် လက်မှတ်ထိုးဝင်သည့်အချက်အလက်ကို သိမ်းမလား။"</string>
<string name="passkey" msgid="632353688396759522">"လျှို့ဝှက်ကီး"</string>
<string name="password" msgid="6738570945182936667">"စကားဝှက်"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"လျှို့ဝှက်ကီးများ"</string>
+ <string name="passwords" msgid="5419394230391253816">"စကားဝှက်များ"</string>
<string name="sign_ins" msgid="4710739369149469208">"လက်မှတ်ထိုးဝင်မှုများ"</string>
<string name="sign_in_info" msgid="2627704710674232328">"လက်မှတ်ထိုးဝင်သည့် အချက်အလက်"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> သိမ်းမည့်နေရာ"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"အခြားစက်ပစ္စည်းတွင် လျှို့ဝှက်ကီး ပြုလုပ်မလား။"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"သင်၏လက်မှတ်ထိုးဝင်မှု အားလုံးအတွက် <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> သုံးမလား။"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"သင်အလွယ်တကူ လက်မှတ်ထိုးဝင်နိုင်ရန် ဤစကားဝှက်မန်နေဂျာက စကားဝှက်နှင့် လျှို့ဝှက်ကီးများကို သိမ်းပေးပါမည်"</string>
<string name="set_as_default" msgid="4415328591568654603">"မူရင်းအဖြစ် သတ်မှတ်ရန်"</string>
<string name="use_once" msgid="9027366575315399714">"တစ်ကြိမ်သုံးရန်"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"စကားဝှက် <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ခု • လျှို့ဝှက်ကီး <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ခု"</string>
diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml
index 34a81e8..8627030 100644
--- a/packages/CredentialManager/res/values-nb/strings.xml
+++ b/packages/CredentialManager/res/values-nb/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Avbryt"</string>
<string name="string_continue" msgid="1346732695941131882">"Fortsett"</string>
<string name="string_more_options" msgid="7990658711962795124">"Flere alternativer"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Tryggere med tilgangsnøkler"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Med tilgangsnøkler trenger du ikke å lage eller huske kompliserte passord"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Tilgangsnøkler er krypterte digitale nøkler du oppretter med fingeravtrykket, ansiktet eller skjermlåsen"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"De lagres i et verktøy for passordlagring, slik at du kan logge på andre enheter"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Velg hvor du vil lagre <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Velg et verktøy for passordlagring for å lagre informasjonen din og logge på raskere neste gang"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vil du opprette en tilgangsnøkkel for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Vil du lagre passord for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vil du lagre påloggingsinformasjon for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"tilgangsnøkkel"</string>
<string name="password" msgid="6738570945182936667">"passord"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"tilgangsnøkler"</string>
+ <string name="passwords" msgid="5419394230391253816">"passord"</string>
<string name="sign_ins" msgid="4710739369149469208">"pålogginger"</string>
<string name="sign_in_info" msgid="2627704710674232328">"påloggingsinformasjon"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Lagre <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> i"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Vil du opprette en tilgangsnøkkel på en annen enhet?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Vil du bruke <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for alle pålogginger?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Dette verktøyet for passordlagring lagrer passord og tilgangsnøkler, så det blir lett å logge på"</string>
<string name="set_as_default" msgid="4415328591568654603">"Angi som standard"</string>
<string name="use_once" msgid="9027366575315399714">"Bruk én gang"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passord • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> tilgangsnøkler"</string>
diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml
index f98ffff9..b8d63e1 100644
--- a/packages/CredentialManager/res/values-ne/strings.xml
+++ b/packages/CredentialManager/res/values-ne/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"रद्द गर्नुहोस्"</string>
<string name="string_continue" msgid="1346732695941131882">"जारी राख्नुहोस्"</string>
<string name="string_more_options" msgid="7990658711962795124">"थप विकल्पहरू"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"पासकीका सहायताले सुरक्षित रहनुहोस्"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"तपाईंले पासकी बनाउनुभयो भने तपाईंले जटिल पासवर्ड बनाउनु वा तिनलाई याद गरिराख्नु पर्दैन"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"पासकी भनेको तपाईंले आफ्नो फिंगरप्रिन्ट, अनुहार वा स्क्रिन लक प्रयोग गरेर बनाएको इन्क्रिप्ट गरिएको डिजिटल की हो"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"तपाईं अन्य डिभाइसहरूमा साइन इन गर्न सक्नुहोस् भन्नाका लागि तिनलाई पासवर्ड म्यानेजरमा सेभ गरिन्छन्"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"तपाईं आफ्ना <xliff:g id="CREATETYPES">%1$s</xliff:g> कहाँ सेभ गर्न चाहनुहुन्छ भन्ने कुरा छनौट गर्नुहोस्"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"कुनै पासवर्ड म्यानेजरमा आफ्नो जानकारी सेभ गरी अर्को टपक अझ छिटो साइन एन गर्नुहोस्"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> को पासकी बनाउने हो?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> को पासवर्ड सेभ गर्ने हो?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> मा साइन गर्न प्रयोग गरिनु पर्ने जानकारी सेभ गर्ने हो?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"पासकीहरू"</string>
+ <string name="passwords" msgid="5419394230391253816">"पासवर्डहरू"</string>
<string name="sign_ins" msgid="4710739369149469208">"साइन इनसम्बन्धी जानकारी"</string>
<string name="sign_in_info" msgid="2627704710674232328">"साइन इन गर्न प्रयोग गरिने जानकारी"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> यहाँ सेभ गर्नुहोस्:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"अर्को डिभाइसमा पासकी बनाउने हो?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"तपाईंले साइन इन गर्ने सबै डिभाइसहरूमा <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> प्रयोग गर्ने हो?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"तपाईं सजिलैसँग साइन इन गर्न सक्नुहोस् भन्नाका लागि यो पासवर्ड म्यानेजरले तपाईंका पासवर्ड र पासकीहरू सेभ गर्छ"</string>
<string name="set_as_default" msgid="4415328591568654603">"डिफल्ट जानकारीका रूपमा सेट गर्नुहोस्"</string>
<string name="use_once" msgid="9027366575315399714">"एक पटक प्रयोग गर्नुहोस्"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> वटा पासवर्ड • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> वटा पासकी"</string>
diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml
index 6bc731b..adea5a3 100644
--- a/packages/CredentialManager/res/values-nl/strings.xml
+++ b/packages/CredentialManager/res/values-nl/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"Annuleren"</string>
<string name="string_continue" msgid="1346732695941131882">"Doorgaan"</string>
<string name="string_more_options" msgid="7990658711962795124">"Meer opties"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Meer informatie"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Veiliger met toegangssleutels"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Met toegangssleutels hoef je geen ingewikkelde wachtwoorden te maken of te onthouden"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Toegangssleutels zijn versleutelde digitale sleutels die je maakt met je vingerafdruk, gezicht of schermvergrendeling"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Ze worden opgeslagen in een wachtwoordmanager zodat je op andere apparaten kunt inloggen"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Meer informatie over toegangssleutels"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Wachtwoordloze technologie"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Met een toegangssleutel heb je geen wachtwoord meer nodig om in te loggen. Als je op deze manier wilt inloggen, moet je je identiteit bevestigen met je vingerafdruk, gezichtsherkenning, pincode of swipepatroon en een toegangssleutel maken."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Cryptografie met openbare sleutels"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Toegangssleutels maken gebruik van cryptografische sleutelparen in overeenstemming met de FIDO Alliance (waartoe onder andere Google, Apple en Microsoft behoren) en W3C-standaarden. Anders dan de combinatie van gebruikersnaam en de tekenreeks die het wachtwoord vormt, wordt bij toegangssleutels voor elke app of website een privé/openbaar sleutelpaar gemaakt. De privésleutel wordt beveiligd opgeslagen op je apparaat of in de wachtwoordmanager om je identiteit te bevestigen. De openbare sleutel wordt gedeeld met de server van de app of website. Als de sleutels overeenkomen, kun je je meteen registreren en inloggen."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Verbeterde accountbeveiliging"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Elke sleutel is exclusief gekoppeld aan de app of website waarvoor deze is gemaakt. Je kunt dus nooit per ongeluk inloggen bij een bedrieglijke app of website. Bovendien bewaren servers alleen openbare sleutels, wat hacken een stuk lastiger maakt."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Naadloze overgang"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"We zijn op weg naar een wachtwoordloze toekomst, maar naast toegangssleutels kun je nog steeds gebruikmaken van wachtwoorden."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"Kies waar je je <xliff:g id="CREATETYPES">%1$s</xliff:g> wilt opslaan"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Selecteer een wachtwoordmanager om je informatie op te slaan en de volgende keer sneller in te loggen"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Toegangssleutel maken voor <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Wachtwoord opslaan voor <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Inloggegevens opslaan voor <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"toegangssleutel"</string>
<string name="password" msgid="6738570945182936667">"wachtwoord"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"toegangssleutels"</string>
+ <string name="passwords" msgid="5419394230391253816">"wachtwoorden"</string>
<string name="sign_ins" msgid="4710739369149469208">"inloggegevens"</string>
<string name="sign_in_info" msgid="2627704710674232328">"inloggegevens"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> opslaan in"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Toegangssleutel maken op een ander apparaat?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> elke keer gebruiken als je inlogt?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Deze wachtwoordmanager slaat je wachtwoorden en toegangssleutels op zodat je makkelijk kunt inloggen"</string>
<string name="set_as_default" msgid="4415328591568654603">"Instellen als standaard"</string>
<string name="use_once" msgid="9027366575315399714">"Eén keer gebruiken"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wachtwoorden • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> toegangssleutels"</string>
diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml
index f2655c8..6d8af5f 100644
--- a/packages/CredentialManager/res/values-or/strings.xml
+++ b/packages/CredentialManager/res/values-or/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="string_continue" msgid="1346732695941131882">"ଜାରି ରଖନ୍ତୁ"</string>
<string name="string_more_options" msgid="7990658711962795124">"ଅଧିକ ବିକଳ୍ପ"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"ପାସକୀ ସହ ଅଧିକ ସୁରକ୍ଷିତ"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"ପାସକୀଗୁଡ଼ିକ ସହ ଆପଣଙ୍କୁ ଜଟିଳ ପାସୱାର୍ଡଗୁଡ଼ିକ ତିଆରି କରିବା କିମ୍ବା ମନେରଖିବାର ଆବଶ୍ୟକତା ନାହିଁ"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"ପାସକୀଗୁଡ଼ିକ ହେଉଛି ଆପଣ ଆପଣଙ୍କ ଟିପଚିହ୍ନ, ଫେସ କିମ୍ବା ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରି ତିଆରି କରୁଥିବା ଏକକ୍ରିପ୍ଟ କରାଯାଇଥିବା ଡିଜିଟାଲ କୀ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ସେଗୁଡ଼ିକୁ ଏକ Password Managerରେ ସେଭ କରାଯାଏ, ଯାହା ଫଳରେ ଆପଣ ଅନ୍ୟ ଡିଭାଇସଗୁଡ଼ିକରେ ସାଇନ ଇନ କରିପାରିବେ"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"ଆପଣଙ୍କ <xliff:g id="CREATETYPES">%1$s</xliff:g>କୁ କେଉଁଠାରେ ସେଭ କରିବେ ତାହା ବାଛନ୍ତୁ"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"ଆପଣଙ୍କ ସୂଚନା ସେଭ କରି ପରବର୍ତ୍ତୀ ସମୟରେ ଶୀଘ୍ର ସାଇନ ଇନ କରିବା ପାଇଁ ଏକ Password Manager ଚୟନ କରନ୍ତୁ"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> ପାଇଁ ପାସକୀ ତିଆରି କରିବେ?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> ପାଇଁ ପାସୱାର୍ଡ ସେଭ କରିବେ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> ପାଇଁ ସାଇନ-ଇନର ସୂଚନା ସେଭ କରିବେ?"</string>
<string name="passkey" msgid="632353688396759522">"ପାସକୀ"</string>
<string name="password" msgid="6738570945182936667">"ପାସୱାର୍ଡ"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ପାସକୀଗୁଡ଼ିକ"</string>
+ <string name="passwords" msgid="5419394230391253816">"ପାସୱାର୍ଡଗୁଡ଼ିକ"</string>
<string name="sign_ins" msgid="4710739369149469208">"ସାଇନ-ଇନ"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ସାଇନ-ଇନ ସୂଚନା"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>କୁ ଏଥିରେ ସେଭ କରନ୍ତୁ"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"ଅନ୍ୟ ଏକ ଡିଭାଇସରେ ପାସକୀ ତିଆରି କରିବେ?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ଆପଣଙ୍କ ସମସ୍ତ ସାଇନ-ଇନ ପାଇଁ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବେ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"ଏହି Password Manager ସହଜରେ ସାଇନ ଇନ କରିବାରେ ଆପଣଙ୍କୁ ସାହାଯ୍ୟ କରିବା ପାଇଁ ଆପଣଙ୍କ ପାସୱାର୍ଡ ଏବଂ ପାସକୀଗୁଡ଼ିକୁ ଷ୍ଟୋର କରିବ"</string>
<string name="set_as_default" msgid="4415328591568654603">"ଡିଫଲ୍ଟ ଭାବେ ସେଟ କରନ୍ତୁ"</string>
<string name="use_once" msgid="9027366575315399714">"ଥରେ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ଟି ପାସୱାର୍ଡ • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>ଟି ପାସକୀ"</string>
diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml
index 824b5db..c77130c 100644
--- a/packages/CredentialManager/res/values-pa/strings.xml
+++ b/packages/CredentialManager/res/values-pa/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"ਰੱਦ ਕਰੋ"</string>
<string name="string_continue" msgid="1346732695941131882">"ਜਾਰੀ ਰੱਖੋ"</string>
<string name="string_more_options" msgid="7990658711962795124">"ਹੋਰ ਵਿਕਲਪ"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"ਪਾਸਕੀਆਂ ਨਾਲ ਸੁਰੱਖਿਅਤ"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"ਪਾਸਕੀਆਂ ਨਾਲ, ਤੁਹਾਨੂੰ ਜਟਿਲ ਪਾਸਵਰਡ ਬਣਾਉਣ ਜਾਂ ਯਾਦ ਰੱਖਣ ਦੀ ਲੋੜ ਨਹੀਂ ਹੁੰਦੀ"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"ਪਾਸਕੀਆਂ ਇਨਕ੍ਰਿਪਟਡ ਡਿਜੀਟਲ ਕੁੰਜੀਆਂ ਹੁੰਦੀਆਂ ਹਨ ਜੋ ਤੁਹਾਡੇ ਵੱਲੋਂ ਤੁਹਾਡੇ ਫਿੰਗਰਪ੍ਰਿੰਟ, ਚਿਹਰੇ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਬਣਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ਉਨ੍ਹਾਂ ਨੂੰ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ \'ਤੇ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਜੋ ਤੁਸੀਂ ਹੋਰ ਡੀਵਾਈਸਾਂ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰ ਸਕੋ"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"ਚੁਣੋ ਕਿ ਆਪਣੇ <xliff:g id="CREATETYPES">%1$s</xliff:g> ਨੂੰ ਕਿੱਥੇ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"ਆਪਣੀ ਜਾਣਕਾਰੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਅਤੇ ਅਗਲੀ ਵਾਰ ਤੇਜ਼ੀ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ ਚੁਣੋ"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ਕੀ <xliff:g id="APPNAME">%1$s</xliff:g> ਲਈ ਪਾਸਕੀ ਬਣਾਉਣੀ ਹੈ?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"ਕੀ <xliff:g id="APPNAME">%1$s</xliff:g> ਲਈ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"ਕੀ <xliff:g id="APPNAME">%1$s</xliff:g> ਲਈ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰਨੀ ਹੈ?"</string>
<string name="passkey" msgid="632353688396759522">"ਪਾਸਕੀ"</string>
<string name="password" msgid="6738570945182936667">"ਪਾਸਵਰਡ"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ਪਾਸਕੀਆਂ"</string>
+ <string name="passwords" msgid="5419394230391253816">"ਪਾਸਵਰਡ"</string>
<string name="sign_ins" msgid="4710739369149469208">"ਸਾਈਨ-ਇਨਾਂ ਦੀ ਜਾਣਕਾਰੀ"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ਨੂੰ ਇੱਥੇ ਰੱਖਿਅਤ ਕਰੋ"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"ਕੀ ਕਿਸੇ ਹੋਰ ਡੀਵਾਈਸ ਨਾਲ ਪਾਸਕੀ ਬਣਾਉਣੀ ਹੈ?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ਕੀ ਆਪਣੇ ਸਾਰੇ ਸਾਈਨ-ਇਨਾਂ ਲਈ<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"ਇਹ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ ਆਸਾਨੀ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਪਾਸਵਰਡਾਂ ਅਤੇ ਪਾਸਕੀਆਂ ਨੂੰ ਸਟੋਰ ਕਰੇਗਾ"</string>
<string name="set_as_default" msgid="4415328591568654603">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
<string name="use_once" msgid="9027366575315399714">"ਇੱਕ ਵਾਰ ਵਰਤੋ"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ਪਾਸਵਰਡ • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ਪਾਸਕੀਆਂ"</string>
diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml
index 8ba182d..9f857d1 100644
--- a/packages/CredentialManager/res/values-pl/strings.xml
+++ b/packages/CredentialManager/res/values-pl/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Anuluj"</string>
<string name="string_continue" msgid="1346732695941131882">"Dalej"</string>
<string name="string_more_options" msgid="7990658711962795124">"Więcej opcji"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Klucze zwiększają Twoje bezpieczeństwo"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Dzięki kluczom nie musisz tworzyć ani zapamiętywać skomplikowanych haseł"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Klucze są szyfrowanymi kluczami cyfrowymi, które tworzysz za pomocą funkcji rozpoznawania odcisku palca lub twarzy bądź blokady ekranu"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Klucze są zapisane w menedżerze haseł, dzięki czemu możesz logować się na innych urządzeniach"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Wybierz, gdzie zapisywać <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Wybierz menedżera haseł, aby zapisywać informacje i logować się szybciej"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Utworzyć klucz dla aplikacji <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Zapisać hasło do aplikacji <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Zapisać dane logowania do aplikacji <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"klucz"</string>
<string name="password" msgid="6738570945182936667">"hasło"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"klucze"</string>
+ <string name="passwords" msgid="5419394230391253816">"hasła"</string>
<string name="sign_ins" msgid="4710739369149469208">"dane logowania"</string>
<string name="sign_in_info" msgid="2627704710674232328">"informacje dotyczące logowania"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Zapisać: <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> w:"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Utworzyć klucz na innym urządzeniu?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Używać usługi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> w przypadku wszystkich danych logowania?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Menedżer haseł będzie zapisywał Twoje hasła i klucze, aby ułatwić Ci logowanie"</string>
<string name="set_as_default" msgid="4415328591568654603">"Ustaw jako domyślną"</string>
<string name="use_once" msgid="9027366575315399714">"Użyj raz"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Hasła: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Klucze: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml
index 7aac738..7deefbb 100644
--- a/packages/CredentialManager/res/values-pt-rBR/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
<string name="string_more_options" msgid="7990658711962795124">"Mais opções"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Mais segurança com as chaves de acesso"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Com as chaves de acesso, você não precisa criar nem se lembrar de senhas complexas"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"As chaves de acesso são chaves digitais criptografadas que você cria usando a impressão digital, o rosto ou o bloqueio de tela"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Elas são salvas em um gerenciador de senhas para que você possa fazer login em outros dispositivos"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"Escolha onde salvar suas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecione um gerenciador de senhas para salvar suas informações e fazer login mais rapidamente na próxima vez"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Criar uma chave de acesso para o app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml
index df8477e..6ad17e0 100644
--- a/packages/CredentialManager/res/values-pt-rPT/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
<string name="string_more_options" msgid="7990658711962795124">"Mais opções"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Saber mais"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Mais segurança com chaves de acesso"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Com as chaves de acesso, não precisa de criar nem memorizar palavras-passe complexas"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"As chaves de acesso são chaves digitais encriptadas que cria através da sua impressão digital, rosto ou bloqueio de ecrã"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"São guardadas num gestor de palavras-passe para que possa iniciar sessão noutros dispositivos"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Mais acerca das chaves de acesso"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Tecnologia sem palavras-passe"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"As chaves de acesso permitem-lhe iniciar sessão sem depender das palavras-passe. Basta usar a impressão digital, o reconhecimento facial, o PIN ou o padrão de deslize para validar a sua identidade e criar uma chave de acesso."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Criptografia de chaves públicas"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Baseadas na FIDO Alliance e no W3C, as palavras de acesso usam pares de chaves criptográficas. Ao contrário do nome de utilizador e da string de carateres das palavras-passe, cria-se um par de chaves públicas/privadas para a app ou Website. A chave privada é armazenada de forma segura no dispositivo ou gestor de palavras-passe e confirma a identidade. A chave pública é partilhada com o servidor do Website ou app. Com as chaves correspondentes, pode registar-se e iniciar sessão instantaneamente."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Segurança melhorada nas contas"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Cada chave é exclusivamente associada à app ou ao Website para o qual foi criada, por isso, nunca pode iniciar sessão numa app ou num Website fraudulento acidentalmente. Além disso, os servidores só mantêm chaves públicas, o que dificulta a pirataria."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Transição sem complicações"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"À medida que avançamos para um futuro sem palavras-passe, as palavras-passe continuam disponíveis juntamente com as chaves de acesso."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Escolha onde guardar as suas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecione um gestor de palavras-passe para guardar as suas informações e iniciar sessão mais rapidamente da próxima vez"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Criar uma chave de acesso para a app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml
index 7aac738..7deefbb 100644
--- a/packages/CredentialManager/res/values-pt/strings.xml
+++ b/packages/CredentialManager/res/values-pt/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
<string name="string_more_options" msgid="7990658711962795124">"Mais opções"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Mais segurança com as chaves de acesso"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Com as chaves de acesso, você não precisa criar nem se lembrar de senhas complexas"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"As chaves de acesso são chaves digitais criptografadas que você cria usando a impressão digital, o rosto ou o bloqueio de tela"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Elas são salvas em um gerenciador de senhas para que você possa fazer login em outros dispositivos"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"Escolha onde salvar suas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecione um gerenciador de senhas para salvar suas informações e fazer login mais rapidamente na próxima vez"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Criar uma chave de acesso para o app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml
index 10ff472..73bddea 100644
--- a/packages/CredentialManager/res/values-ro/strings.xml
+++ b/packages/CredentialManager/res/values-ro/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Anulează"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuă"</string>
<string name="string_more_options" msgid="7990658711962795124">"Mai multe opțiuni"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Mai în siguranță cu chei de acces"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Dacă folosești chei de acces, nu este nevoie să creezi sau să reții parole complexe"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Cheile de acces sunt chei digitale criptate pe care le creezi folosindu-ți amprenta, fața sau blocarea ecranului"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Acestea sunt salvate într-un manager de parole, ca să te poți conecta pe alte dispozitive"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Alege unde dorești să salvezi <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Selectează un manager de parole pentru a salva informațiile și a te conecta mai rapid data viitoare"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Creezi o cheie de acces pentru <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Salvezi parola pentru <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Salvezi informațiile de conectare pentru <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"cheie de acces"</string>
<string name="password" msgid="6738570945182936667">"parolă"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"cheile de acces"</string>
+ <string name="passwords" msgid="5419394230391253816">"parolele"</string>
<string name="sign_ins" msgid="4710739369149469208">"date de conectare"</string>
<string name="sign_in_info" msgid="2627704710674232328">"informațiile de conectare"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Salvează <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> în"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Creezi cheia de acces pe alt dispozitiv?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Folosești <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pentru toate conectările?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Managerul de parole îți va stoca parolele și cheile de acces, pentru a te ajuta să te conectezi cu ușurință"</string>
<string name="set_as_default" msgid="4415328591568654603">"Setează ca prestabilite"</string>
<string name="use_once" msgid="9027366575315399714">"Folosește o dată"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parole • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chei de acces"</string>
diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml
index 9a0d979..3f12657 100644
--- a/packages/CredentialManager/res/values-ru/strings.xml
+++ b/packages/CredentialManager/res/values-ru/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"Отмена"</string>
<string name="string_continue" msgid="1346732695941131882">"Продолжить"</string>
<string name="string_more_options" msgid="7990658711962795124">"Ещё"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Подробнее"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Ключи доступа безопаснее"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Благодаря ключам доступа вам не придется создавать или запоминать сложные пароли."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Ключ доступа – это зашифрованное цифровое удостоверение, которое создается с использованием отпечатка пальца, функции фейсконтроля или блокировки экрана."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Данные хранятся в менеджере паролей, чтобы вы могли входить в аккаунт на других устройствах."</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Подробнее о ключах доступа"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Технология аутентификации без пароля"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Ключи доступа позволяют входить в аккаунт без ввода пароля. Чтобы подтвердить личность и создать ключ доступа, достаточно использовать отпечаток пальца, распознавание по лицу, PIN-код или графический ключ."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Шифрование с помощью открытого ключа"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Согласно стандартам W3C и ассоциации FIDO Alliance (Google, Apple, Microsoft и др.) для создания ключей доступа используются пары криптографических ключей. В приложении или на сайте создается не имя пользователя и пароль, а пара открытого и закрытого ключей. Закрытый ключ хранится на вашем устройстве или в менеджере паролей и нужен для подтверждения личности. Открытый ключ передается приложению или серверу сайта. Подходящие ключи помогают быстро войти в аккаунт или зарегистрироваться."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Повышенная безопасность аккаунта"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Каждый ключ связан только с тем приложением или сайтом, для которого был создан, поэтому вы не сможете по ошибке войти в приложение или на сайт мошенников. Кроме того, на серверах хранятся только открытые ключи, что служит дополнительной защитой от взлома."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Плавный переход"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Хотя движение к будущему без паролей уже началось, их по-прежнему можно будет использовать наряду с ключами доступа."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"Укажите, куда нужно сохранить <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Выберите менеджер паролей, чтобы сохранять учетные данные и быстро выполнять вход."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Создать ключ доступа для приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Сохранить пароль для приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Сохранить учетные данные для приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступа"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ключи доступа"</string>
+ <string name="passwords" msgid="5419394230391253816">"пароли"</string>
<string name="sign_ins" msgid="4710739369149469208">"входы"</string>
<string name="sign_in_info" msgid="2627704710674232328">"учетные данные"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Сохранить <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> в"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Создать ключ доступа на другом устройстве?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Всегда входить с помощью приложения \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"В этом менеджере паролей можно сохранять учетные данные, например ключи доступа, чтобы потом использовать их для быстрого входа."</string>
<string name="set_as_default" msgid="4415328591568654603">"Использовать по умолчанию"</string>
<string name="use_once" msgid="9027366575315399714">"Использовать один раз"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Пароли (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) и ключи доступа (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string>
diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml
index 11a7a38..64038ca 100644
--- a/packages/CredentialManager/res/values-si/strings.xml
+++ b/packages/CredentialManager/res/values-si/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"අවලංගු කරන්න"</string>
<string name="string_continue" msgid="1346732695941131882">"ඉදිරියට යන්න"</string>
<string name="string_more_options" msgid="7990658711962795124">"තවත් විකල්ප"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"තව දැන ගන්න"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"මුරයතුරු සමග සුරක්ෂිතයි"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"මුරයතුරු සමග, ඔබට සංකීර්ණ මුරපද තැනීමට හෝ මතක තබා ගැනීමට අවශ්ය නොවේ"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"මුරයතුරු යනු ඔබේ ඇඟිලි සලකුණ, මුහුණ, හෝ තිර අගුල භාවිතයෙන් ඔබ තනන සංකේතාත්මක ඩිජිටල් යතුරු වේ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ඒවා මුරපද කළමනාකරුවෙකු වෙත සුරකින බැවින්, ඔබට වෙනත් උපාංග මත පුරනය විය හැක"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"මුරයතුරු ගැන වැඩි විස්තර"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"මුරපද රහිත තාක්ෂණය"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"මුරයතුරු මත විශ්වාසය නොතබා පුරනය වීමට මුරපද ඔබට ඉඩ සලසයි. ඔබට ඔබේ අනන්යතාව තහවුරු කර ගැනීමට සහ මුරයතුරක් සෑදීමට ඔබේ ඇඟිලි සලකුණ, මුහුණු හඳුනාගැනීම, රහස් අංකය, හෝ ස්වයිප් රටාව භාවිත කිරීමට අවශ්ය වේ."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"පොදු යතුරු ගුප්ත කේතනය"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO සන්ධාන (Google, Apple, Microsoft, සහ තවත් ඒවා ඇතුළත්) සහ W3C සම්මත මත පදනම්ව, මුරයතුරු ගුප්ත කේතන යතුරු යුගල භාවිත කරයි. අපි මුරපද සඳහා භාවිත කරන පරිශීලක නාමය සහ අනුලකුණු මෙන් නොව, යෙදුමක් හෝ වෙබ් අඩවියක් සඳහා පෞද්ගලික පොදු යතුරු යුගලයක් සාදනු ලැබේ. පෞද්ගලික යතුර ආරක්ෂිතව ඔබේ උපාංගයේ හෝ මුරපද කළමනාකරු මත ගබඩා කර ඇති අතර එය ඔබේ අනන්යතාව තහවුරු කරයි. යෙදුම හෝ වෙබ් අඩවි සේවාදායකය සමග පොදු යතුර බෙදාගෙන ඇත. අනුරූප යතුරු සමග, ඔබට ක්ෂණිකව ලියාපදිංචි වී පුරනය විය හැක."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"වැඩිදියුණු කළ ගිණුම් ආරක්ෂාව"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"සෑම යතුරක්ම ඒවා නිර්මාණය කරන ලද යෙදුම හෝ වෙබ් අඩවිය සමග අනන්ය වශයෙන්ම සම්බන්ධ කර ඇත, එබැවින් ඔබට කිසි විටෙක වැරදීමකින් වංචනික යෙදුමකට හෝ වෙබ් අඩවියකට පුරනය විය නොහැක. ඊට අමතරව, සේවාදායකයින් පොදු යතුරු තබා ගැනීමත් සමග, අනවසරයෙන් ඇතුළුවීම වඩා දුෂ්කරය."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"බාධාවකින් තොර සංක්රමණය"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"අපි මුරපද රහිත අනාගතයක් කරා ගමන් කරන විට, මුරයතුරු සමග මුරපද තවමත් පවතී."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"ඔබේ <xliff:g id="CREATETYPES">%1$s</xliff:g> සුරැකිය යුතු ස්ථානය තෝරා ගන්න"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"ඔබේ තතු සුරැකීමට සහ මීළඟ වතාවේ වේගයෙන් පුරනය වීමට මුරපද කළමනාකරුවෙකු තෝරන්න"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> සඳහා මුරයතුර තනන්න ද?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> සඳහා මුරපදය සුරකින්න ද?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> සඳහා පුරනය වීමේ තතු සුරකින්න ද?"</string>
<string name="passkey" msgid="632353688396759522">"මුරයතුර"</string>
<string name="password" msgid="6738570945182936667">"මුරපදය"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"මුරයතුරු"</string>
+ <string name="passwords" msgid="5419394230391253816">"මුරපද"</string>
<string name="sign_ins" msgid="4710739369149469208">"පුරනය වීම්"</string>
<string name="sign_in_info" msgid="2627704710674232328">"පුරනය වීමේ තතු"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"වෙත <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> සුරකින්න"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"වෙනත් උපාංගයක මුරයතුර තනන්න ද?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"ඔබේ සියලු පුරනය වීම් සඳහා <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> භාවිතා කරන්න ද?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"මෙම මුරපද කළමනාකරු ඔබට පහසුවෙන් පුරනය වීමට උදවු කිරීම සඳහා ඔබේ මුරපද සහ මුරයතුරු ගබඩා කරනු ඇත"</string>
<string name="set_as_default" msgid="4415328591568654603">"පෙරනිමි ලෙස සකසන්න"</string>
<string name="use_once" msgid="9027366575315399714">"වරක් භාවිතා කරන්න"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"මුරපද <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ක් • මුරයතුරු <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>ක්"</string>
diff --git a/packages/CredentialManager/res/values-sk/strings.xml b/packages/CredentialManager/res/values-sk/strings.xml
index eba5779..635f0047 100644
--- a/packages/CredentialManager/res/values-sk/strings.xml
+++ b/packages/CredentialManager/res/values-sk/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"Zrušiť"</string>
<string name="string_continue" msgid="1346732695941131882">"Pokračovať"</string>
<string name="string_more_options" msgid="7990658711962795124">"Ďalšie možnosti"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"Ďalšie informácie"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Bezpečnejšie s prístupovými kľúčmi"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Ak máte prístupové kľúče, nemusíte vytvárať ani si pamätať zložité heslá"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Prístupové kľúče sú šifrované digitálne kľúče, ktoré môžete vytvoriť odtlačkom prsta, tvárou alebo zámkou obrazovky."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Ukladajú sa do správcu hesiel, aby ste sa mohli prihlasovať v iných zariadeniach"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Viac o prístupových kľúčoch"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"Technológia bez hesiel"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Prístupový kľúče vám umožňujú prihlásiť sa bez využitia hesiel. Stačí overiť totožnosť odtlačkom prsta, rozpoznávaním tváre, kódom PIN alebo vzorom potiahnutia a vytvoriť prístupový kľúč."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"Kryptografia verejných kľúčov"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Prístup. kľúče firiem patriacich do združenia FIDO (zahŕňajúceho Google, Apple, Microsoft a ďalšie) využívajú páry kryptograf. kľúčov a štandardy W3C. Na rozdiel od použív. mena a reťazca znakov využívaných v prípade hesiel sa pár súkr. a verej. kľúča vytvára pre aplikáciu alebo web. Súkr. kľúč sa bezpečne ukladá v zar. či správcovi hesiel a slúži na overenie totožnosti. Verej. kľúč sa zdieľa so serverom danej aplik. alebo webu. Príslušnými kľúčami sa môžete okamžite registrovať a prihlasovať."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"Lepšie zabezpečenie účtu"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Každý kľúč je výhradne prepojený s aplikáciou alebo webom, pre ktorý bol vytvorený, takže sa nikdy nemôžete omylom prihlásiť do podvodnej aplikácie alebo na podvodnom webe. Servery navyše uchovávajú iba verejné kľúče, čím podstatne sťažujú hackovanie."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"Plynulý prechod"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Blížime sa k budúcnosti bez hesiel, ale heslá budú popri prístupových kľúčoch stále k dispozícii."</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"Vyberte, kam sa majú ukladať <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Vyberte správcu hesiel, do ktorého sa budú ukladať vaše údaje, aby ste sa nabudúce mohli rýchlejšie prihlásiť"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Chcete vytvoriť prístupový kľúč pre aplikáciu <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Chcete uložiť heslo pre aplikáciu <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Chcete uložiť prihlasovacie údaje pre aplikáciu <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"prístupový kľúč"</string>
<string name="password" msgid="6738570945182936667">"heslo"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"prístupové kľúče"</string>
+ <string name="passwords" msgid="5419394230391253816">"heslá"</string>
<string name="sign_ins" msgid="4710739369149469208">"prihlasovacie údaje"</string>
<string name="sign_in_info" msgid="2627704710674232328">"prihlasovacie údaje"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Uložiť <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> do"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Chcete vytvoriť prístupový kľúč v inom zariadení?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Chcete pre všetky svoje prihlasovacie údaje použiť <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Tento správca hesiel uchová vaše heslá a prístupové kľúče, aby vám pomohol ľahšie sa prihlasovať"</string>
<string name="set_as_default" msgid="4415328591568654603">"Nastaviť ako predvolené"</string>
<string name="use_once" msgid="9027366575315399714">"Použiť raz"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Počet hesiel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Počet prístupových kľúčov: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-sl/strings.xml b/packages/CredentialManager/res/values-sl/strings.xml
index 7201f32..a8561f4 100644
--- a/packages/CredentialManager/res/values-sl/strings.xml
+++ b/packages/CredentialManager/res/values-sl/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Prekliči"</string>
<string name="string_continue" msgid="1346732695941131882">"Naprej"</string>
<string name="string_more_options" msgid="7990658711962795124">"Več možnosti"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Večja varnost s ključi za dostop"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Po zaslugi ključev za dostop vam ni treba ustvarjati ali si zapomniti zapletenih gesel."</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Ključi za dostop so šifrirani digitalni ključi, ki jih ustvarite s prstnim odtisom, obrazom ali načinom zaklepanja zaslona."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Shranjeni so v upravitelju gesel, kar pomeni, da se z njimi lahko prijavite tudi v drugih napravah."</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Izbira mesta za shranjevanje <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Izberite upravitelja gesel za shranjevanje podatkov za prijavo, da se boste naslednjič lahko hitreje prijavili."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Želite ustvariti ključ za dostop do aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Želite shraniti geslo za aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Želite shraniti podatke za prijavo za aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ključ za dostop"</string>
<string name="password" msgid="6738570945182936667">"geslo"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ključev za dostop"</string>
+ <string name="passwords" msgid="5419394230391253816">"gesel"</string>
<string name="sign_ins" msgid="4710739369149469208">"prijave"</string>
<string name="sign_in_info" msgid="2627704710674232328">"podatkov za prijavo"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Mesto shranjevanja <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Želite ustvariti ključ za dostop v drugi napravi?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite za vse prijave uporabiti »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"V tem upravitelju gesel bodo shranjeni gesla in ključi za dostop, kar vam bo olajšalo prijavo."</string>
<string name="set_as_default" msgid="4415328591568654603">"Nastavi kot privzeto"</string>
<string name="use_once" msgid="9027366575315399714">"Uporabi enkrat"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Št. gesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Št. ključev za dostop: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml
index 87cc2fe..897450e 100644
--- a/packages/CredentialManager/res/values-sq/strings.xml
+++ b/packages/CredentialManager/res/values-sq/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Anulo"</string>
<string name="string_continue" msgid="1346732695941131882">"Vazhdo"</string>
<string name="string_more_options" msgid="7990658711962795124">"Opsione të tjera"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Më e sigurt me çelësat e kalimit"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Me çelësat e kalimit, nuk ka nevojë të krijosh ose të mbash mend fjalëkalime të ndërlikuara"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Çelësat e kalimit kanë çelësa dixhitalë të enkriptuar që ti i krijon duke përdorur gjurmën e gishtit, fytyrën ose kyçjen e ekranit"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Ata ruhen te një menaxher fjalëkalimesh, në mënyrë që mund të identifikohesh në pajisje të tjera"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Zgjidh se ku të ruash <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Zgjidh një menaxher fjalëkalimesh për të ruajtur informacionet e tua dhe për t\'u identifikuar më shpejt herën tjetër"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Të krijohet çelësi i kalimit për <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Të ruhet fjalëkalimi për <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Të ruhen informacionet e identifikimit për <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"çelësi i kalimit"</string>
<string name="password" msgid="6738570945182936667">"fjalëkalimi"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"çelësa kalimi"</string>
+ <string name="passwords" msgid="5419394230391253816">"fjalëkalime"</string>
<string name="sign_ins" msgid="4710739369149469208">"identifikimet"</string>
<string name="sign_in_info" msgid="2627704710674232328">"informacionet e identifikimit"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Ruaj <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> te"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Të krijohet çelës kalimi në një pajisje tjetër?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Të përdoret <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> për të gjitha identifikimet?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Ky menaxher i fjalëkalimeve do të ruajë fjalëkalimet dhe çelësat e kalimit për të të ndihmuar të identifikohesh me lehtësi"</string>
<string name="set_as_default" msgid="4415328591568654603">"Cakto si parazgjedhje"</string>
<string name="use_once" msgid="9027366575315399714">"Përdor një herë"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> fjalëkalime • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> çelësa kalimi"</string>
diff --git a/packages/CredentialManager/res/values-sr/strings.xml b/packages/CredentialManager/res/values-sr/strings.xml
index 80c0537..375d97dc 100644
--- a/packages/CredentialManager/res/values-sr/strings.xml
+++ b/packages/CredentialManager/res/values-sr/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Откажи"</string>
<string name="string_continue" msgid="1346732695941131882">"Настави"</string>
<string name="string_more_options" msgid="7990658711962795124">"Још опција"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Безбедније уз приступне кодове"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Уз приступне кодове нема потребе да правите или памтите сложене лозинке"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Приступни кодови су шифровани дигитални кодови које правите помоћу отиска прста, лица или закључавања екрана"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Чувају се у менаџеру лозинки да бисте могли да се пријављујете на другим уређајима"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Одаберите где ћете сачувати ставке <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Изаберите менаџера лозинки да бисте сачували податке и брже се пријавили следећи пут"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Желите да направите приступни кôд за: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Желите да сачувате лозинку за: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Желите да сачувате податке за пријављивање за: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"приступни кôд"</string>
<string name="password" msgid="6738570945182936667">"лозинка"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"приступни кодови"</string>
+ <string name="passwords" msgid="5419394230391253816">"лозинке"</string>
<string name="sign_ins" msgid="4710739369149469208">"пријављивања"</string>
<string name="sign_in_info" msgid="2627704710674232328">"подаци за пријављивање"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Сачувајте ставку<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> у"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Желите да направите приступни кôд на другом уређају?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Желите да за сва пријављивања користите: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Овај менаџер лозинки ће чувати лозинке и приступне кодове да бисте се лако пријављивали"</string>
<string name="set_as_default" msgid="4415328591568654603">"Подеси као подразумевано"</string>
<string name="use_once" msgid="9027366575315399714">"Користи једном"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Лозинки: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Приступних кодова:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml
index ab8c5aa..d1a043d 100644
--- a/packages/CredentialManager/res/values-sv/strings.xml
+++ b/packages/CredentialManager/res/values-sv/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Avbryt"</string>
<string name="string_continue" msgid="1346732695941131882">"Fortsätt"</string>
<string name="string_more_options" msgid="7990658711962795124">"Fler alternativ"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Säkrare med nycklar"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Med nycklar behöver du inte skapa eller komma ihop invecklade lösenord"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Nycklar är krypterade digitala nycklar som du skapar med ditt fingeravtryck, ansikte eller skärmlås"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"De sparas i lösenordshanteraren så att du kan logga in på andra enheter"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"Välj var du vill spara <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Välj en lösenordshanterare för att spara dina uppgifter och logga in snabbare nästa gång"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vill du skapa en nyckel för <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-sw/strings.xml b/packages/CredentialManager/res/values-sw/strings.xml
index 1685882..68af50b 100644
--- a/packages/CredentialManager/res/values-sw/strings.xml
+++ b/packages/CredentialManager/res/values-sw/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Ghairi"</string>
<string name="string_continue" msgid="1346732695941131882">"Endelea"</string>
<string name="string_more_options" msgid="7990658711962795124">"Chaguo zaidi"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Ni salama ukitumia funguo za siri"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Kwa kutumia funguo za siri, huhitaji kuunda au kukumbuka manenosiri changamano"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Funguo za siri ni funguo dijitali zilizosimbwa kwa njia fiche unazounda kwa kutumia alama yako ya kidole, uso au mbinu ya kufunga skrini"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Vyote huhifadhiwa kwenye kidhibiti cha manenosiri, ili uweze kuingia katika akaunti kwenye vifaa vingine"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Chagua ambako unahifadhi <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Chagua kidhibiti cha manenosiri ili uhifadhi taarifa zako na uingie kwenye akaunti kwa urahisi wakati mwingine"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Ungependa kuunda ufunguo wa siri kwa ajili ya <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Ungependa kuhifadhi nenosiri kwa ajili ya <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Ungependa kuhifadhi maelezo ya kuingia katika akaunti kwa ajili ya <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ufunguo wa siri"</string>
<string name="password" msgid="6738570945182936667">"nenosiri"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"funguo za siri"</string>
+ <string name="passwords" msgid="5419394230391253816">"manenosiri"</string>
<string name="sign_ins" msgid="4710739369149469208">"michakato ya kuingia katika akaunti"</string>
<string name="sign_in_info" msgid="2627704710674232328">"maelezo ya kuingia katika akaunti"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Hifadhi <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> kwenye"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Ungependa kuunda ufunguo wa siri kwenye kifaa kingine?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Ungependa kutumia <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kwa ajili ya michakato yako yote ya kuingia katika akaunti?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Kidhibiti hiki cha manenosiri kitahifadhi manenosiri na funguo zako za siri ili kukusaidia uingie katika akaunti kwa urahisi"</string>
<string name="set_as_default" msgid="4415328591568654603">"Weka iwe chaguomsingi"</string>
<string name="use_once" msgid="9027366575315399714">"Tumia mara moja"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Manenosiri <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • funguo <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> za siri"</string>
diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml
index 5c68f66..9857215 100644
--- a/packages/CredentialManager/res/values-ta/strings.xml
+++ b/packages/CredentialManager/res/values-ta/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"ரத்துசெய்"</string>
<string name="string_continue" msgid="1346732695941131882">"தொடர்க"</string>
<string name="string_more_options" msgid="7990658711962795124">"கூடுதல் விருப்பங்கள்"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"கடவுச்சாவிகள் மூலம் பாதுகாப்பாக வைத்திருங்கள்"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"கடவுச்சாவிகளைப் பயன்படுத்துவதன் மூலம் கடினமான கடவுச்சொற்களை உருவாக்கவோ நினைவில்கொள்ளவோ தேவையில்லை"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"கடவுச்சாவிகள் என்பவை உங்கள் கைரேகை, முகம் அல்லது திரைப் பூட்டு மூலம் உருவாக்கப்படும் என்க்ரிப்ஷன் செய்யப்பட்ட டிஜிட்டல் சாவிகள் ஆகும்"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"அவை Password Managerரில் சேமிக்கப்பட்டுள்ளதால் நீங்கள் பிற சாதனங்களிலிருந்து உள்நுழையலாம்"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"உங்கள் <xliff:g id="CREATETYPES">%1$s</xliff:g> எங்கே சேமிக்கப்பட வேண்டும் என்பதைத் தேர்வுசெய்யுங்கள்"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"உங்கள் தகவல்களைச் சேமித்து அடுத்த முறை விரைவாக உள்நுழைய ஒரு கடவுச்சொல் நிர்வாகியைத் தேர்வுசெய்யுங்கள்"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சாவியை உருவாக்கவா?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சொல்லைச் சேமிக்கவா?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸுக்கான உள்நுழைவு விவரங்களைச் சேமிக்கவா?"</string>
<string name="passkey" msgid="632353688396759522">"கடவுக்குறியீடு"</string>
<string name="password" msgid="6738570945182936667">"கடவுச்சொல்"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"கடவுச்சாவிகள்"</string>
+ <string name="passwords" msgid="5419394230391253816">"கடவுச்சொற்கள்"</string>
<string name="sign_ins" msgid="4710739369149469208">"உள்நுழைவுகள்"</string>
<string name="sign_in_info" msgid="2627704710674232328">"உள்நுழைவு விவரங்கள்"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ஐ இதில் சேமியுங்கள்"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"வேறொரு சாதனத்தில் கடவுச்சாவியை உருவாக்க வேண்டுமா?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"உங்கள் அனைத்து உள்நுழைவுகளுக்கும் <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ஐப் பயன்படுத்தவா?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"இந்தக் கடவுச்சொல் நிர்வாகி உங்கள் கடவுச்சொற்களையும் கடவுச்சாவிகளையும் சேமித்து நீங்கள் எளிதாக உள்நுழைய உதவும்"</string>
<string name="set_as_default" msgid="4415328591568654603">"இயல்பானதாக அமை"</string>
<string name="use_once" msgid="9027366575315399714">"ஒருமுறை பயன்படுத்தவும்"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> கடவுச்சொற்கள் • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> கடவுச்சாவிகள்"</string>
diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml
index f428ced..e62bac9 100644
--- a/packages/CredentialManager/res/values-te/strings.xml
+++ b/packages/CredentialManager/res/values-te/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"రద్దు చేయండి"</string>
<string name="string_continue" msgid="1346732695941131882">"కొనసాగించండి"</string>
<string name="string_more_options" msgid="7990658711962795124">"మరిన్ని ఆప్షన్లు"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"మరింత తెలుసుకోండి"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"పాస్-కీలతో సురక్షితంగా చెల్లించవచ్చు"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"పాస్-కీలతో, మీరు క్లిష్టమైన పాస్వర్డ్లను క్రియేట్ చేయనవసరం లేదు లేదా గుర్తుంచుకోనవసరం లేదు"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"పాస్-కీలు అనేవి మీ వేలిముద్రను, ముఖాన్ని లేదా స్క్రీన్ లాక్ను ఉపయోగించి మీరు క్రియేట్ చేసే ఎన్క్రిప్ట్ చేసిన డిజిటల్ కీలు"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"అవి Password Managerకు సేవ్ చేయబడతాయి, తద్వారా మీరు ఇతర పరికరాలలో సైన్ ఇన్ చేయవచ్చు"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"పాస్-కీల గురించి మరిన్ని వివరాలు"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"పాస్వర్డ్ రహిత టెక్నాలజీ"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"పాస్వర్డ్లపై ఆధారపడకుండా సైన్ ఇన్ చేయడానికి పాస్-కీలు మిమ్మల్ని అనుమతిస్తాయి. మీ గుర్తింపును వెరిఫై చేసి, పాస్-కీని క్రియేట్ చేయడానికి మీరు మీ వేలిముద్ర, ముఖ గుర్తింపు, PIN, లేదా స్వైప్ ఆకృతిని ఉపయోగించాలి."</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"పబ్లిక్ కీ క్రిప్టోగ్రఫీ"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO Alliance (దీనిలో Google, Apple, Microsoft, మరిన్ని ఉన్నాయి), W3C ప్రమాణాల ప్రకారం, పాస్కీలు క్రిప్టోగ్రాఫిక్ కీల జతలను ఉపయోగిస్తాయి. మనం పాస్వర్డ్ల కోసం ఉపయోగించే యూజర్నేమ్, అక్షరాల స్ట్రింగ్ కాకుండా, యాప్ లేదా సైట్ కోసం ప్రైవేట్-పబ్లిక్ కీల జత సృష్టించబడుతుంది. ప్రైవేట్ కీ మీ డివైజ్/పాస్వర్డ్ మేనేజర్లో సురక్షితంగా స్టోర్ చేయబడుతుంది, ఇది మీ గుర్తింపును నిర్ధారిస్తుంది. పబ్లిక్ కీ, యాప్/వెబ్సైట్ సర్వర్తో షేర్ చేయబడుతుంది. సంబంధిత కీలతో, తక్షణమే రిజిస్టర్ చేసుకొని, సైన్ ఇన్ చేయవచ్చు."</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"మెరుగైన ఖాతా సెక్యూరిటీ"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"ప్రతి కీ దానిని క్రియేట్ చేసిన యాప్ లేదా వెబ్సైట్తో ప్రత్యేకంగా లింక్ చేయబడి ఉంటుంది, కాబట్టి మీరు పొరపాటున కూడా మోసపూరిత యాప్ లేదా వెబ్సైట్కు సైన్ ఇన్ చేయలేరు. అంతే కాకుండా, సర్వర్లు పబ్లిక్ కీలను మాత్రమే స్టోర్ చేయడం వల్ల, హ్యాకింగ్ చేయడం చాలా కష్టం."</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"అవాంతరాలు లేని పరివర్తన"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"మనం భవిష్యత్తులో పాస్వర్డ్ రహిత టెక్నాలజీని ఉపయోగించినా, పాస్కీలతో పాటు పాస్వర్డ్లు కూడా అందుబాటులో ఉంటాయి."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"మీ <xliff:g id="CREATETYPES">%1$s</xliff:g>ని ఎక్కడ సేవ్ చేయాలో ఎంచుకోండి"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"మీ సమాచారాన్ని సేవ్ చేయడం కోసం ఒక Password Managerను ఎంచుకొని, తర్వాతసారి వేగంగా సైన్ ఇన్ చేయండి"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> కోసం పాస్కీని క్రియేట్ చేయాలా?"</string>
diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml
index 5362d48..61144f9 100644
--- a/packages/CredentialManager/res/values-th/strings.xml
+++ b/packages/CredentialManager/res/values-th/strings.xml
@@ -5,10 +5,20 @@
<string name="string_cancel" msgid="6369133483981306063">"ยกเลิก"</string>
<string name="string_continue" msgid="1346732695941131882">"ต่อไป"</string>
<string name="string_more_options" msgid="7990658711962795124">"ตัวเลือกเพิ่มเติม"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"ดูข้อมูลเพิ่มเติม"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"ปลอดภัยขึ้นด้วยพาสคีย์"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"พาสคีย์ช่วยให้คุณไม่ต้องสร้างหรือจำรหัสผ่านที่ซับซ้อน"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"พาสคีย์คือกุญแจดิจิทัลที่เข้ารหัสซึ่งคุณสร้างโดยใช้ลายนิ้วมือ ใบหน้า หรือการล็อกหน้าจอ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ข้อมูลนี้จะบันทึกอยู่ในเครื่องมือจัดการรหัสผ่านเพื่อให้คุณลงชื่อเข้าใช้ในอุปกรณ์อื่นๆ ได้"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"ข้อมูลเพิ่มเติมเกี่ยวกับพาสคีย์"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"เทคโนโลยีที่ไม่ต้องใช้รหัสผ่าน"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"พาสคีย์ช่วยให้คุณลงชื่อเข้าใช้ได้โดยไม่ต้องใช้รหัสผ่าน ใช้เพียงแค่ลายนิ้วมือ, การจดจำใบหน้า, PIN หรือรูปแบบการลากเส้นในการยืนยันตัวตนและสร้างพาสคีย์"</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"วิทยาการเข้ารหัสคีย์สาธารณะ"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"พาสคีย์ใช้คู่คีย์การเข้ารหัสตามมาตรฐานของ FIDO Alliance (เช่น Google, Apple, Microsoft และอื่นๆ) และ W3C คู่คีย์สาธารณะและคีย์ส่วนตัวจะสร้างขึ้นสำหรับแอปหรือเว็บไซต์ที่ใช้งานคีย์ดังกล่าวต่างจากชื่อผู้ใช้และชุดอักขระที่ใช้เป็นรหัสผ่าน โดยระบบจะจัดเก็บคีย์ส่วนตัวไว้ในอุปกรณ์หรือเครื่องมือจัดการรหัสผ่านและใช้คีย์ดังกล่าวเพื่อยืนยันตัวตน ส่วนคีย์สาธารณะจะแชร์กับเซิร์ฟเวอร์ของแอปหรือเว็บไซต์ ลงทะเบียนและลงชื่อเข้าใช้ได้ทันทีด้วยคีย์ที่สอดคล้องกัน"</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"ความปลอดภัยของบัญชีที่เพิ่มมากขึ้น"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"คีย์ที่สร้างขึ้นแต่ละคีย์จะลิงก์กับแอปหรือเว็บไซต์ที่ใช้งานคีย์ดังกล่าวเท่านั้น ดังนั้นจึงไม่มีการลงชื่อเข้าใช้แอปเว็บไซต์ที่เป็นการฉ้อโกงโดยไม่ตั้งใจเกิดขึ้น นอกจากนี้ เซิร์ฟเวอร์จะบันทึกเฉพาะคีย์สาธารณะ จึงทำให้แฮ็กได้ยากขึ้น"</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"การเปลี่ยนผ่านอย่างราบรื่น"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"ในขณะที่เราก้าวไปสู่อนาคตที่ไม่ต้องใช้รหัสผ่านนั้น รหัสผ่านจะยังคงใช้ได้อยู่ควบคู่ไปกับการเปลี่ยนไปใช้พาสคีย์"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"เลือกว่าต้องการบันทึก<xliff:g id="CREATETYPES">%1$s</xliff:g>ไว้ที่ใด"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"เลือกเครื่องมือจัดการรหัสผ่านเพื่อบันทึกข้อมูลและลงชื่อเข้าใช้เร็วขึ้นในครั้งถัดไป"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"สร้างพาสคีย์สำหรับ <xliff:g id="APPNAME">%1$s</xliff:g> ไหม"</string>
diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml
index 633c3a0..1c78fb8 100644
--- a/packages/CredentialManager/res/values-tl/strings.xml
+++ b/packages/CredentialManager/res/values-tl/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Kanselahin"</string>
<string name="string_continue" msgid="1346732695941131882">"Magpatuloy"</string>
<string name="string_more_options" msgid="7990658711962795124">"Higit pang opsyon"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Mas ligtas gamit ang mga passkey"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Gamit ang mga passkey, hindi mo na kailangang gumawa o tumanda ng mga komplikadong password"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Ang mga passkey ay mga naka-encrypt na digital na susi na ginagawa mo gamit ang iyong fingerprint, mukha, o lock ng screen"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Sine-save ang mga ito sa isang password manager para makapag-sign in ka sa iba pang device"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"Piliin kung saan mo ise-save ang iyong <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Pumili ng password manger para ma-save ang iyong impormasyon at makapag-sign in nang mas mabilis sa susunod na pagkakataon"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Gumawa ng passkey para sa <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml
index 3627e6c..5a3bcba 100644
--- a/packages/CredentialManager/res/values-tr/strings.xml
+++ b/packages/CredentialManager/res/values-tr/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"İptal"</string>
<string name="string_continue" msgid="1346732695941131882">"Devam"</string>
<string name="string_more_options" msgid="7990658711962795124">"Diğer seçenekler"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Şifre anahtarlarıyla daha yüksek güvenlik"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Şifre anahtarı kullandığınızda karmaşık şifreler oluşturmanız veya bunları hatırlamanız gerekmez"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Şifre anahtarları; parmak iziniz, yüzünüz veya ekran kilidinizi kullanarak oluşturduğunuz şifrelenmiş dijital anahtarlardır"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Diğer cihazlarda oturum açabilmeniz için şifre anahtarları bir şifre yöneticisine kaydedilir"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> bilgilerinizin kaydedileceği yeri seçin"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Bilgilerinizi kaydedip bir dahaki sefere daha hızlı oturum açmak için bir şifre yöneticisi seçin"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> için şifre anahtarı oluşturulsun mu?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> için şifre kaydedilsin mi?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> için oturum açma bilgileri kaydedilsin mi?"</string>
<string name="passkey" msgid="632353688396759522">"şifre anahtarı"</string>
<string name="password" msgid="6738570945182936667">"şifre"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"şifre anahtarları"</string>
+ <string name="passwords" msgid="5419394230391253816">"şifreler"</string>
<string name="sign_ins" msgid="4710739369149469208">"oturum aç"</string>
<string name="sign_in_info" msgid="2627704710674232328">"oturum açma bilgileri"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Şuraya kaydet: <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Başka bir cihazda şifre anahtarı oluşturulsun mu?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Tüm oturum açma işlemlerinizde <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kullanılsın mı?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Bu şifre yöneticisi, şifrelerinizi ve şifre anahtarlarınızı saklayarak kolayca oturum açmanıza yardımcı olur"</string>
<string name="set_as_default" msgid="4415328591568654603">"Varsayılan olarak ayarla"</string>
<string name="use_once" msgid="9027366575315399714">"Bir kez kullanın"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> şifre • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> şifre anahtarı"</string>
diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml
index 49ef6c1..46e7649 100644
--- a/packages/CredentialManager/res/values-uk/strings.xml
+++ b/packages/CredentialManager/res/values-uk/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Скасувати"</string>
<string name="string_continue" msgid="1346732695941131882">"Продовжити"</string>
<string name="string_more_options" msgid="7990658711962795124">"Інші опції"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Ключі доступу покращують безпеку"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Маючи ключі доступу, не потрібно створювати чи запам’ятовувати складні паролі"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Ключі доступу – це зашифровані цифрові ключі, які ви створюєте за допомогою відбитка пальця, фейс-контролю чи засобу розблокування екрана"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Вони зберігаються в менеджері паролів, тож ви можете входити на інших пристроях"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Виберіть, де зберігати <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Виберіть менеджер паролів, щоб зберігати свої дані й надалі входити в облікові записи швидше"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Створити ключ доступу для додатка <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Зберегти пароль для додатка <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Зберегти дані для входу для додатка <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"ключі доступу"</string>
+ <string name="passwords" msgid="5419394230391253816">"паролі"</string>
<string name="sign_ins" msgid="4710739369149469208">"дані для входу"</string>
<string name="sign_in_info" msgid="2627704710674232328">"дані для входу"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Зберегти <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> в"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Створити ключ доступу на іншому пристрої?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Використовувати сервіс <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> в усіх випадках входу?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Цей менеджер паролів зберігатиме ваші паролі та ключі доступу, щоб ви могли легко входити в облікові записи"</string>
<string name="set_as_default" msgid="4415328591568654603">"Вибрати за умовчанням"</string>
<string name="use_once" msgid="9027366575315399714">"Скористатися раз"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Кількість паролів: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • Кількість ключів доступу: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml
index 8ceb842..103ea38 100644
--- a/packages/CredentialManager/res/values-ur/strings.xml
+++ b/packages/CredentialManager/res/values-ur/strings.xml
@@ -5,31 +5,35 @@
<string name="string_cancel" msgid="6369133483981306063">"منسوخ کریں"</string>
<string name="string_continue" msgid="1346732695941131882">"جاری رکھیں"</string>
<string name="string_more_options" msgid="7990658711962795124">"مزید اختیارات"</string>
+ <string name="string_learn_more" msgid="4541600451688392447">"مزید جانیں"</string>
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"پاس کیز کے ساتھ زیادہ محفوظ"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"پاس کیز کے ساتھ آپ کو پیچیدہ پاس ورڈز تخلیق کرنے یا انہیں یاد رکھنے کی ضرورت نہیں ہے"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"پاس کیز مرموز کردہ ڈیجیٹل کلیدیں ہیں جنہیں آپ اپنا فنگر پرنٹ، چہرہ یا اسکرین لاک استعمال کرتے ہوئے تخلیق کرتے ہیں"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"ان کو پاس ورڈ مینیجر میں محفوظ کیا جاتا ہے تاکہ آپ دوسرے آلات پر سائن ان کر سکیں"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
- <skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
- <skip />
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"پاس کیز کے بارے میں مزید"</string>
+ <string name="passwordless_technology_title" msgid="2497513482056606668">"بغیر پاس ورڈ والی ٹیکنالوجی"</string>
+ <string name="passwordless_technology_detail" msgid="6853928846532955882">"پاس کیز کے ذریعے آپ پاس ورڈز پر اعتماد کیے بغیر سائن ان کر سکتے ہیں۔ اپنی شناخت کی توثیق کرنے اور پاس کی تخلیق کرنے کے لیے آپ کو صرف اپنے فنگر پرنٹ، چہرے کی شناخت، PIN استعمال کرنے کی ضرورت ہے یا پیٹرن سوائپ کریں۔"</string>
+ <string name="public_key_cryptography_title" msgid="6751970819265298039">"عوامی کلید کی کرپٹو گرافی"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"FIDO اتحاد (جس میں Google, Apple, Microsoft وغیرہ شامل ہیں) اور W3C معیارات کی بنیاد پر، پاس کیز کرپٹوگرافک کلیدوں کا جوڑا استعمال کرتی ہیں۔ صارف نام اور حروف کی اسٹرنگ کے برعکس جو ہم پاس ورڈز کے لیے استعمال کرتے ہیں، ایک ایپ یا ویب سائٹ کے لیے ایک نجی عوامی کلیدوں کا جوڑا تخلیق کیا جاتا ہے۔ نجی کلید آپ کے آلے یا پاس ورڈ مینیجر پر محفوظ طریقے سے اسٹور ہوتی ہے اور یہ آپ کی شناخت کی تصدیق کرتی ہے۔ عوامی کلید ایپ یا ویب سائٹ کے سرور کے ساتھ اشتراک کی جاتی ہے۔ متعلقہ کلیدوں کے ساتھ، آپ فوری طور پر رجسٹر اور سائن ان کر سکتے ہیں۔"</string>
+ <string name="improved_account_security_title" msgid="1069841917893513424">"بہتر کردہ اکاؤنٹ کی سیکیورٹی"</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"ہر کلید خصوصی طور پر اس ایپ یا ویب سائٹ سے منسلک ہے جس کے لیے اسے تخلیق کیا گیا تھا، اس لیے آپ کبھی بھی غلطی سے کسی پر فریب ایپ یا ویب سائٹ میں سائن ان نہیں کر سکتے ہیں۔ اس کے علاوہ، چونکہ سرورز صرف عوامی کلید رکھتے ہیں، اس لیے ہیکنگ بہت مشکل ہے۔"</string>
+ <string name="seamless_transition_title" msgid="5335622196351371961">"آسان ٹرانزیشن"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"چونکہ ہم بغیر پاس ورڈ والے مستقبل کی طرف جا رہے ہیں اس کے باوجود پاس ورڈز پاس کیز کے ساتھ ہی دستیاب ہوں گے۔"</string>
+ <string name="choose_provider_title" msgid="8870795677024868108">"منتخب کریں کہ آپ کی <xliff:g id="CREATETYPES">%1$s</xliff:g> کو کہاں محفوظ کرنا ہے"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"اپنی معلومات کو محفوظ کرنے اور اگلی بار تیزی سے سائن ان کرنے کے لیے پاس ورڈ مینیجر منتخب کریں"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> کے لیے پاس کی تخلیق کریں؟"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> کے لیے پاس ورڈ کو محفوظ کریں؟"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> کے لیے سائن ان کی معلومات محفوظ کریں؟"</string>
<string name="passkey" msgid="632353688396759522">"پاس کی"</string>
<string name="password" msgid="6738570945182936667">"پاس ورڈ"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"پاس کیز"</string>
+ <string name="passwords" msgid="5419394230391253816">"پاس ورڈز"</string>
<string name="sign_ins" msgid="4710739369149469208">"سائن انز"</string>
<string name="sign_in_info" msgid="2627704710674232328">"سائن ان کی معلومات"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> کو اس میں محفوظ کریں"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"کسی دوسرے آلے میں پاس کی تخلیق کریں؟"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"اپنے سبھی سائن انز کے لیے <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> کا استعمال کریں؟"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"آسانی سے سائن ان کرنے میں آپ کی مدد کرنے کے لیے یہ پاس ورڈ مینیجر آپ کے پاس ورڈز اور پاس کیز کو اسٹور کرے گا"</string>
<string name="set_as_default" msgid="4415328591568654603">"بطور ڈیفالٹ سیٹ کریں"</string>
<string name="use_once" msgid="9027366575315399714">"ایک بار استعمال کریں"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> پاس ورڈز • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> پاس کیز"</string>
diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml
index 9233a53..f815f67 100644
--- a/packages/CredentialManager/res/values-uz/strings.xml
+++ b/packages/CredentialManager/res/values-uz/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"Bekor qilish"</string>
<string name="string_continue" msgid="1346732695941131882">"Davom etish"</string>
<string name="string_more_options" msgid="7990658711962795124">"Boshqa parametrlar"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Kalitlar orqali qulay"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Kodlar yordami tufayli murakkab parollarni yaratish va eslab qolish shart emas"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Kodlar – bu barmoq izi, yuz yoki ekran qulfi yordamida yaratilgan shifrlangan raqamli identifikator."</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Ular parollar menejerida saqlanadi va ulardan boshqa qurilmalarda hisobga kirib foydalanish mumkin"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> qayerga saqlanishini tanlang"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Maʼlumotlaringizni saqlash va keyingi safar tez kirish uchun parollar menejerini tanlang"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> uchun kod yaratilsinmi?"</string>
diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml
index 94111a7..0fe35b1 100644
--- a/packages/CredentialManager/res/values-vi/strings.xml
+++ b/packages/CredentialManager/res/values-vi/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Huỷ"</string>
<string name="string_continue" msgid="1346732695941131882">"Tiếp tục"</string>
<string name="string_more_options" msgid="7990658711962795124">"Tuỳ chọn khác"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"An toàn hơn nhờ mã xác thực"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Mã xác thực giúp bạn tránh được việc phải tạo và ghi nhớ mật khẩu phức tạp"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Mã xác thực là các khoá kỹ thuật số được mã hoá mà bạn tạo bằng cách dùng vân tay, khuôn mặt hoặc phương thức khoá màn hình của mình"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Thông tin này được lưu vào trình quản lý mật khẩu nên bạn có thể đăng nhập trên các thiết bị khác"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Chọn vị trí lưu <xliff:g id="CREATETYPES">%1$s</xliff:g> của bạn"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Hãy chọn một trình quản lý mật khẩu để lưu thông tin của bạn và đăng nhập nhanh hơn trong lần tới"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Tạo mã xác thực cho <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Lưu mật khẩu cho <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Lưu thông tin đăng nhập cho <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"mã xác thực"</string>
<string name="password" msgid="6738570945182936667">"mật khẩu"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"mã xác thực"</string>
+ <string name="passwords" msgid="5419394230391253816">"mật khẩu"</string>
<string name="sign_ins" msgid="4710739369149469208">"thông tin đăng nhập"</string>
<string name="sign_in_info" msgid="2627704710674232328">"thông tin đăng nhập"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Lưu <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> vào"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Tạo mã xác thực trên thiết bị khác?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Dùng <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> cho mọi thông tin đăng nhập của bạn?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Trình quản lý mật khẩu này sẽ lưu trữ mật khẩu và mã xác thực của bạn để bạn dễ dàng đăng nhập"</string>
<string name="set_as_default" msgid="4415328591568654603">"Đặt làm mặc định"</string>
<string name="use_once" msgid="9027366575315399714">"Dùng một lần"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mật khẩu • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> mã xác thực"</string>
diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml
index 1fc42bd..78a3d22 100644
--- a/packages/CredentialManager/res/values-zh-rCN/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"取消"</string>
<string name="string_continue" msgid="1346732695941131882">"继续"</string>
<string name="string_more_options" msgid="7990658711962795124">"更多选项"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"通行密钥可提高安全性"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"借助通行密钥,您无需创建或记住复杂的密码"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"通行密钥是指您使用您的指纹、面孔或屏锁方式创建的加密数字钥匙"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"系统会将通行密钥保存到密码管理工具中,以便您在其他设备上登录"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"选择保存<xliff:g id="CREATETYPES">%1$s</xliff:g>的位置"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"请选择一款密码管理工具来保存您的信息,以便下次更快地登录"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"要为“<xliff:g id="APPNAME">%1$s</xliff:g>”创建通行密钥吗?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"要保存“<xliff:g id="APPNAME">%1$s</xliff:g>”的密码吗?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"要保存“<xliff:g id="APPNAME">%1$s</xliff:g>”的登录信息吗?"</string>
<string name="passkey" msgid="632353688396759522">"通行密钥"</string>
<string name="password" msgid="6738570945182936667">"密码"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"通行密钥"</string>
+ <string name="passwords" msgid="5419394230391253816">"密码"</string>
<string name="sign_ins" msgid="4710739369149469208">"登录"</string>
<string name="sign_in_info" msgid="2627704710674232328">"登录信息"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"将<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>保存到"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"在其他设备上创建通行密钥?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"将“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”用于您的所有登录信息?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"此密码管理工具将会存储您的密码和通行密钥,以帮助您轻松登录"</string>
<string name="set_as_default" msgid="4415328591568654603">"设为默认项"</string>
<string name="use_once" msgid="9027366575315399714">"使用一次"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 个密码 • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 个通行密钥"</string>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
index 276b936..54fe97c 100644
--- a/packages/CredentialManager/res/values-zh-rHK/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -5,25 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"取消"</string>
<string name="string_continue" msgid="1346732695941131882">"繼續"</string>
<string name="string_more_options" msgid="7990658711962795124">"更多選項"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"使用密鑰確保帳戶安全"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"有了密鑰,您便無需建立或記住複雜的密碼"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"密鑰是您使用指紋、面孔或螢幕鎖定時建立的加密數碼鑰匙"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"密鑰已儲存至密碼管理工具,方便您在其他裝置上登入"</string>
- <string name="choose_provider_title" msgid="8870795677024868108">"選擇要將<xliff:g id="CREATETYPES">%1$s</xliff:g>存在哪裡"</string>
- <string name="choose_provider_body" msgid="4967074531845147434">"選取密碼管理工具並儲存資訊,下次就能更快登入"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"選擇儲存<xliff:g id="CREATETYPES">%1$s</xliff:g>的位置"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"選取密碼管理工具即可儲存自己的資料,縮短下次登入的時間"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"要為「<xliff:g id="APPNAME">%1$s</xliff:g>」建立密鑰嗎?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"要儲存「<xliff:g id="APPNAME">%1$s</xliff:g>」的密碼嗎?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"要儲存「<xliff:g id="APPNAME">%1$s</xliff:g>」的登入資料嗎?"</string>
<string name="passkey" msgid="632353688396759522">"密鑰"</string>
<string name="password" msgid="6738570945182936667">"密碼"</string>
- <string name="passkeys" msgid="5733880786866559847">"密碼金鑰"</string>
+ <string name="passkeys" msgid="5733880786866559847">"密鑰"</string>
<string name="passwords" msgid="5419394230391253816">"密碼"</string>
<string name="sign_ins" msgid="4710739369149469208">"登入資料"</string>
<string name="sign_in_info" msgid="2627704710674232328">"登入資料"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"將<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>儲存至"</string>
- <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"要在其他裝置上建立密碼金鑰嗎?"</string>
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"要在其他裝置上建立密鑰嗎?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"要將「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」用於所有的登入資料嗎?"</string>
- <string name="use_provider_for_all_description" msgid="8466427781848268490">"這個密碼管理工具會儲存密碼和密碼金鑰,協助你輕鬆登入"</string>
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"此密碼管理工具將儲存您的密碼和密鑰,協助您輕鬆登入"</string>
<string name="set_as_default" msgid="4415328591568654603">"設定為預設"</string>
<string name="use_once" msgid="9027366575315399714">"單次使用"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼 • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 個密鑰"</string>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
index 910756e..0be95d2 100644
--- a/packages/CredentialManager/res/values-zh-rTW/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -5,10 +5,30 @@
<string name="string_cancel" msgid="6369133483981306063">"取消"</string>
<string name="string_continue" msgid="1346732695941131882">"繼續"</string>
<string name="string_more_options" msgid="7990658711962795124">"更多選項"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"使用密碼金鑰確保帳戶安全"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"有了密碼金鑰,就不必建立或記住複雜的密碼"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"密碼金鑰是你利用指紋、臉孔或螢幕鎖定功能建立的加密數位金鑰"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"密碼金鑰會儲存到密碼管理工具,方便你在其他裝置上登入"</string>
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
+ <skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
<string name="choose_provider_title" msgid="8870795677024868108">"選擇要將<xliff:g id="CREATETYPES">%1$s</xliff:g>存在哪裡"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"選取密碼管理工具並儲存資訊,下次就能更快登入"</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"要為「<xliff:g id="APPNAME">%1$s</xliff:g>」建立密碼金鑰嗎?"</string>
diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml
index ab56435..f281c0d 100644
--- a/packages/CredentialManager/res/values-zu/strings.xml
+++ b/packages/CredentialManager/res/values-zu/strings.xml
@@ -5,31 +5,45 @@
<string name="string_cancel" msgid="6369133483981306063">"Khansela"</string>
<string name="string_continue" msgid="1346732695941131882">"Qhubeka"</string>
<string name="string_more_options" msgid="7990658711962795124">"Okunye okungakukhethwa kukho"</string>
+ <!-- no translation found for string_learn_more (4541600451688392447) -->
+ <skip />
<string name="passkey_creation_intro_title" msgid="4251037543787718844">"Iphephe ngokhiye bokudlula"</string>
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Ngokhiye wokudlula, awudingi ukusungula noma ukukhumbula amaphasiwedi ayinkimbinkimbi"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Okhiye bokungena bangokhiye bedijithali ababethelwe obasungula usebenzisa isigxivizo somunwe sakho, ubuso, noma ukukhiya isikrini"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Okhiye bokudlula balondolozwa kusiphathi sephasiwedi, ukuze ukwazi ukungena ngemvume kwamanye amadivayisi"</string>
- <!-- no translation found for choose_provider_title (8870795677024868108) -->
+ <!-- no translation found for more_about_passkeys_title (7797903098728837795) -->
<skip />
- <!-- no translation found for choose_provider_body (4967074531845147434) -->
+ <!-- no translation found for passwordless_technology_title (2497513482056606668) -->
<skip />
+ <!-- no translation found for passwordless_technology_detail (6853928846532955882) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_title (6751970819265298039) -->
+ <skip />
+ <!-- no translation found for public_key_cryptography_detail (6937631710280562213) -->
+ <skip />
+ <!-- no translation found for improved_account_security_title (1069841917893513424) -->
+ <skip />
+ <!-- no translation found for improved_account_security_detail (9123750251551844860) -->
+ <skip />
+ <!-- no translation found for seamless_transition_title (5335622196351371961) -->
+ <skip />
+ <!-- no translation found for seamless_transition_detail (4475509237171739843) -->
+ <skip />
+ <string name="choose_provider_title" msgid="8870795677024868108">"Khetha lapho ongagcina khona i-<xliff:g id="CREATETYPES">%1$s</xliff:g> yakho"</string>
+ <string name="choose_provider_body" msgid="4967074531845147434">"Khetha isiphathi sephasiwedi ukuze ulondoloze ulwazi lwakho futhi ungene ngemvume ngokushesha ngesikhathi esizayo."</string>
<string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Sungula ukhiye wokudlula we-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_password_title" msgid="7097275038523578687">"Londolozela amaphasiwedi ye-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Londoloza ulwazi lokungena lwe-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ukhiye wokudlula"</string>
<string name="password" msgid="6738570945182936667">"iphasiwedi"</string>
- <!-- no translation found for passkeys (5733880786866559847) -->
- <skip />
- <!-- no translation found for passwords (5419394230391253816) -->
- <skip />
+ <string name="passkeys" msgid="5733880786866559847">"okhiye bokudlula"</string>
+ <string name="passwords" msgid="5419394230391253816">"amaphasiwedi"</string>
<string name="sign_ins" msgid="4710739369149469208">"ukungena ngemvume"</string>
<string name="sign_in_info" msgid="2627704710674232328">"ulwazi lokungena ngemvume"</string>
<string name="save_credential_to_title" msgid="3172811692275634301">"Londoloza i-<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ku-"</string>
- <!-- no translation found for create_passkey_in_other_device_title (9195411122362461390) -->
- <skip />
+ <string name="create_passkey_in_other_device_title" msgid="9195411122362461390">"Sungula ukhiye wokudlula kwenye idivayisi?"</string>
<string name="use_provider_for_all_title" msgid="4201020195058980757">"Sebenzisa i-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kukho konke ukungena kwakho ngemvume?"</string>
- <!-- no translation found for use_provider_for_all_description (8466427781848268490) -->
- <skip />
+ <string name="use_provider_for_all_description" msgid="8466427781848268490">"Lesi siphathi sephasiwedi sizogcina amaphasiwedi akho nezikhiye zokungena ukuze zikusize ungene ngemvume kalula."</string>
<string name="set_as_default" msgid="4415328591568654603">"Setha njengokuzenzakalelayo"</string>
<string name="use_once" msgid="9027366575315399714">"Sebenzisa kanye"</string>
<string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Amaphasiwedi angu-<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> • okhiye bokudlula abangu-<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 0761b64..a48cd2b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -55,422 +55,457 @@
// Consider repo per screen, similar to view model?
class CredentialManagerRepo(
- private val context: Context,
- intent: Intent,
+ private val context: Context,
+ intent: Intent,
) {
- val requestInfo: RequestInfo
- private val providerEnabledList: List<ProviderData>
- private val providerDisabledList: List<DisabledProviderData>?
- // TODO: require non-null.
- val resultReceiver: ResultReceiver?
+ val requestInfo: RequestInfo
+ private val providerEnabledList: List<ProviderData>
+ private val providerDisabledList: List<DisabledProviderData>?
- init {
- requestInfo = intent.extras?.getParcelable(
- RequestInfo.EXTRA_REQUEST_INFO,
- RequestInfo::class.java
- ) ?: testCreatePasskeyRequestInfo()
+ // TODO: require non-null.
+ val resultReceiver: ResultReceiver?
- providerEnabledList = when (requestInfo.type) {
- RequestInfo.TYPE_CREATE ->
- intent.extras?.getParcelableArrayList(
- ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
- CreateCredentialProviderData::class.java
- ) ?: testCreateCredentialEnabledProviderList()
- RequestInfo.TYPE_GET ->
- intent.extras?.getParcelableArrayList(
- ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
- GetCredentialProviderData::class.java
- ) ?: testGetCredentialProviderList()
- else -> {
- // TODO: fail gracefully
- throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
- }
+ init {
+ requestInfo = intent.extras?.getParcelable(
+ RequestInfo.EXTRA_REQUEST_INFO,
+ RequestInfo::class.java
+ ) ?: testCreatePasskeyRequestInfo()
+
+ providerEnabledList = when (requestInfo.type) {
+ RequestInfo.TYPE_CREATE ->
+ intent.extras?.getParcelableArrayList(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+ CreateCredentialProviderData::class.java
+ ) ?: testCreateCredentialEnabledProviderList()
+ RequestInfo.TYPE_GET ->
+ intent.extras?.getParcelableArrayList(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+ GetCredentialProviderData::class.java
+ ) ?: testGetCredentialProviderList()
+ else -> {
+ // TODO: fail gracefully
+ throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
+ }
+ }
+
+ providerDisabledList =
+ intent.extras?.getParcelableArrayList(
+ ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST,
+ DisabledProviderData::class.java
+ ) ?: testDisabledProviderList()
+
+ resultReceiver = intent.getParcelableExtra(
+ Constants.EXTRA_RESULT_RECEIVER,
+ ResultReceiver::class.java
+ )
}
- providerDisabledList =
- intent.extras?.getParcelableArrayList(
- ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST,
- DisabledProviderData::class.java
- ) ?: testDisabledProviderList()
+ // The dialog is canceled by the user.
+ fun onUserCancel() {
+ onCancel(BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED)
+ }
- resultReceiver = intent.getParcelableExtra(
- Constants.EXTRA_RESULT_RECEIVER,
- ResultReceiver::class.java
- )
- }
+ // The dialog is canceled because we launched into settings.
+ fun onSettingLaunchCancel() {
+ onCancel(BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION)
+ }
- // The dialog is canceled by the user.
- fun onUserCancel() {
- onCancel(BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED)
- }
+ fun onParsingFailureCancel() {
+ onCancel(BaseDialogResult.RESULT_CODE_DATA_PARSING_FAILURE)
+ }
- // The dialog is canceled because we launched into settings.
- fun onSettingLaunchCancel() {
- onCancel(BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION)
- }
+ fun onCancel(cancelCode: Int) {
+ val resultData = Bundle()
+ BaseDialogResult.addToBundle(BaseDialogResult(requestInfo.token), resultData)
+ resultReceiver?.send(cancelCode, resultData)
+ }
- private fun onCancel(cancelCode: Int) {
- val resultData = Bundle()
- BaseDialogResult.addToBundle(BaseDialogResult(requestInfo.token), resultData)
- resultReceiver?.send(cancelCode, resultData)
- }
+ fun onOptionSelected(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ resultCode: Int? = null,
+ resultData: Intent? = null,
+ ) {
+ val userSelectionDialogResult = UserSelectionDialogResult(
+ requestInfo.token,
+ providerId,
+ entryKey,
+ entrySubkey,
+ if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
+ )
+ val resultData = Bundle()
+ UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultData)
+ resultReceiver?.send(
+ BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+ resultData
+ )
+ }
- fun onOptionSelected(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- resultCode: Int? = null,
- resultData: Intent? = null,
- ) {
- val userSelectionDialogResult = UserSelectionDialogResult(
- requestInfo.token,
- providerId,
- entryKey,
- entrySubkey,
- if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
- )
- val resultData = Bundle()
- UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultData)
- resultReceiver?.send(BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION, resultData)
- }
+ fun getCredentialInitialUiState(): GetCredentialUiState? {
+ val providerEnabledList = GetFlowUtils.toProviderList(
+ // TODO: handle runtime cast error
+ providerEnabledList as List<GetCredentialProviderData>, context
+ )
+ val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context)
+ return GetCredentialUiState(
+ providerEnabledList,
+ requestDisplayInfo ?: return null,
+ )
+ }
- fun getCredentialInitialUiState(): GetCredentialUiState {
- val providerEnabledList = GetFlowUtils.toProviderList(
- // TODO: handle runtime cast error
- providerEnabledList as List<GetCredentialProviderData>, context)
- val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context)
- return GetCredentialUiState(
- providerEnabledList,
- requestDisplayInfo,
- )
- }
+ fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
+ val providerEnabledList = CreateFlowUtils.toEnabledProviderList(
+ // Handle runtime cast error
+ providerEnabledList as List<CreateCredentialProviderData>, context
+ )
+ return providerEnabledList
+ }
- fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
- val providerEnabledList = CreateFlowUtils.toEnabledProviderList(
- // Handle runtime cast error
- providerEnabledList as List<CreateCredentialProviderData>, context)
- return providerEnabledList
- }
+ fun getCreateProviderDisableListInitialUiState(): List<DisabledProviderInfo> {
+ return CreateFlowUtils.toDisabledProviderList(
+ // Handle runtime cast error
+ providerDisabledList, context
+ )
+ }
- fun getCreateProviderDisableListInitialUiState(): List<DisabledProviderInfo>? {
- return CreateFlowUtils.toDisabledProviderList(
- // Handle runtime cast error
- providerDisabledList, context)
- }
+ fun getCreateRequestDisplayInfoInitialUiState(): RequestDisplayInfo? {
+ return CreateFlowUtils.toRequestDisplayInfo(requestInfo, context)
+ }
- fun getCreateRequestDisplayInfoInitialUiState(): RequestDisplayInfo {
- return CreateFlowUtils.toRequestDisplayInfo(requestInfo, context)
- }
+ // TODO: below are prototype functionalities. To be removed for productionization.
+ private fun testCreateCredentialEnabledProviderList(): List<CreateCredentialProviderData> {
+ return listOf(
+ CreateCredentialProviderData
+ .Builder("io.enpass.app")
+ .setSaveEntries(
+ listOf<Entry>(
+ newCreateEntry(
+ "key1", "subkey-1", "elisa.beckett@gmail.com",
+ 20, 7, 27, 10L,
+ "Optional footer description"
+ ),
+ newCreateEntry(
+ "key1", "subkey-2", "elisa.work@google.com",
+ 20, 7, 27, 12L,
+ null
+ ),
+ )
+ )
+ .setRemoteEntry(
+ newRemoteEntry("key2", "subkey-1")
+ )
+ .build(),
+ CreateCredentialProviderData
+ .Builder("com.dashlane")
+ .setSaveEntries(
+ listOf<Entry>(
+ newCreateEntry(
+ "key1", "subkey-3", "elisa.beckett@dashlane.com",
+ 20, 7, 27, 11L,
+ null
+ ),
+ newCreateEntry(
+ "key1", "subkey-4", "elisa.work@dashlane.com",
+ 20, 7, 27, 14L,
+ null
+ ),
+ )
+ )
+ .build(),
+ )
+ }
- // TODO: below are prototype functionalities. To be removed for productionization.
- private fun testCreateCredentialEnabledProviderList(): List<CreateCredentialProviderData> {
- return listOf(
- CreateCredentialProviderData
- .Builder("io.enpass.app")
- .setSaveEntries(
- listOf<Entry>(
- newCreateEntry("key1", "subkey-1", "elisa.beckett@gmail.com",
- 20, 7, 27, 10L,
- "Optional footer description"),
- newCreateEntry("key1", "subkey-2", "elisa.work@google.com",
- 20, 7, 27, 12L,
- null),
- )
- )
- .setRemoteEntry(
- newRemoteEntry("key2", "subkey-1")
- )
- .build(),
- CreateCredentialProviderData
- .Builder("com.dashlane")
- .setSaveEntries(
- listOf<Entry>(
- newCreateEntry("key1", "subkey-3", "elisa.beckett@dashlane.com",
- 20, 7, 27, 11L,
- null),
- newCreateEntry("key1", "subkey-4", "elisa.work@dashlane.com",
- 20, 7, 27, 14L,
- null),
- )
- )
- .build(),
- )
- }
+ private fun testDisabledProviderList(): List<DisabledProviderData>? {
+ return listOf(
+ DisabledProviderData("com.lastpass.lpandroid"),
+ DisabledProviderData("com.google.android.youtube")
+ )
+ }
- private fun testDisabledProviderList(): List<DisabledProviderData>? {
- return listOf(
- DisabledProviderData("com.lastpass.lpandroid"),
- DisabledProviderData("com.google.android.youtube")
- )
- }
-
- private fun testGetCredentialProviderList(): List<GetCredentialProviderData> {
- return listOf(
- GetCredentialProviderData.Builder("io.enpass.app")
- .setCredentialEntries(
- listOf<Entry>(
- newGetEntry(
- "key1", "subkey-1", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.family@outlook.com", null, 3L
- ),
- newGetEntry(
- "key1", "subkey-1", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
- "elisa.bakery@gmail.com", "Elisa Beckett", 0L
- ),
- newGetEntry(
- "key1", "subkey-2", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.bakery@gmail.com", null, 10L
- ),
- newGetEntry(
- "key1", "subkey-3", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
- "elisa.family@outlook.com", "Elisa Beckett", 1L
- ),
- )
- ).setAuthenticationEntry(
- newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
- ).setActionChips(
- listOf(
- newActionEntry(
- "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
- "Open Google Password Manager", "elisa.beckett@gmail.com"
- ),
- newActionEntry(
- "key3", "subkey-2", TYPE_PASSWORD_CREDENTIAL,
- "Open Google Password Manager", "beckett-family@gmail.com"
- ),
- )
- ).setRemoteEntry(
- newRemoteEntry("key4", "subkey-1")
- ).build(),
- GetCredentialProviderData.Builder("com.dashlane")
- .setCredentialEntries(
- listOf<Entry>(
- newGetEntry(
- "key1", "subkey-2", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.family@outlook.com", null, 4L
- ),
- newGetEntry(
- "key1", "subkey-3", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.work@outlook.com", null, 11L
- ),
- )
- ).setAuthenticationEntry(
- newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
- ).setActionChips(
- listOf(
- newActionEntry(
- "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
- "Open Enpass"
- ),
- )
- ).build(),
- )
- }
+ private fun testGetCredentialProviderList(): List<GetCredentialProviderData> {
+ return listOf(
+ GetCredentialProviderData.Builder("io.enpass.app")
+ .setCredentialEntries(
+ listOf<Entry>(
+ newGetEntry(
+ "key1", "subkey-1", TYPE_PASSWORD_CREDENTIAL, "Password",
+ "elisa.family@outlook.com", null, 3L
+ ),
+ newGetEntry(
+ "key1", "subkey-1", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
+ "elisa.bakery@gmail.com", "Elisa Beckett", 0L
+ ),
+ newGetEntry(
+ "key1", "subkey-2", TYPE_PASSWORD_CREDENTIAL, "Password",
+ "elisa.bakery@gmail.com", null, 10L
+ ),
+ newGetEntry(
+ "key1", "subkey-3", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
+ "elisa.family@outlook.com", "Elisa Beckett", 1L
+ ),
+ )
+ ).setAuthenticationEntry(
+ newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
+ ).setActionChips(
+ listOf(
+ newActionEntry(
+ "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
+ "Open Google Password Manager", "elisa.beckett@gmail.com"
+ ),
+ newActionEntry(
+ "key3", "subkey-2", TYPE_PASSWORD_CREDENTIAL,
+ "Open Google Password Manager", "beckett-family@gmail.com"
+ ),
+ )
+ ).setRemoteEntry(
+ newRemoteEntry("key4", "subkey-1")
+ ).build(),
+ GetCredentialProviderData.Builder("com.dashlane")
+ .setCredentialEntries(
+ listOf<Entry>(
+ newGetEntry(
+ "key1", "subkey-2", TYPE_PASSWORD_CREDENTIAL, "Password",
+ "elisa.family@outlook.com", null, 4L
+ ),
+ newGetEntry(
+ "key1", "subkey-3", TYPE_PASSWORD_CREDENTIAL, "Password",
+ "elisa.work@outlook.com", null, 11L
+ ),
+ )
+ ).setAuthenticationEntry(
+ newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
+ ).setActionChips(
+ listOf(
+ newActionEntry(
+ "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
+ "Open Enpass"
+ ),
+ )
+ ).build(),
+ )
+ }
private fun newActionEntry(
- key: String,
- subkey: String,
- credentialType: String,
- text: String,
- subtext: String? = null,
+ key: String,
+ subkey: String,
+ credentialType: String,
+ text: String,
+ subtext: String? = null,
): Entry {
val action = Action(text, subtext, null)
return Entry(
- key,
- subkey,
- Action.toSlice(action)
+ key,
+ subkey,
+ Action.toSlice(action)
)
}
private fun newAuthenticationEntry(
- key: String,
- subkey: String,
- credentialType: String,
+ key: String,
+ subkey: String,
+ credentialType: String,
): Entry {
val slice = Slice.Builder(
- Uri.EMPTY, SliceSpec(credentialType, 1)
+ Uri.EMPTY, SliceSpec(credentialType, 1)
)
return Entry(
- key,
- subkey,
- slice.build()
+ key,
+ subkey,
+ slice.build()
)
}
private fun newGetEntry(
- key: String,
- subkey: String,
- credentialType: String,
- credentialTypeDisplayName: String,
- userName: String,
- userDisplayName: String?,
- lastUsedTimeMillis: Long?,
+ key: String,
+ subkey: String,
+ credentialType: String,
+ credentialTypeDisplayName: String,
+ userName: String,
+ userDisplayName: String?,
+ lastUsedTimeMillis: Long?,
): Entry {
val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
- .setPackage("com.androidauth.androidvault")
+ .setPackage("com.androidauth.androidvault")
intent.putExtra("provider_extra_sample", "testprovider")
- val pendingIntent = PendingIntent.getActivity(context, 1,
- intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- or PendingIntent.FLAG_ONE_SHOT))
+ val pendingIntent = PendingIntent.getActivity(
+ context, 1,
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ or PendingIntent.FLAG_ONE_SHOT)
+ )
- val credentialEntry = CredentialEntry(credentialType, credentialTypeDisplayName, userName,
- userDisplayName, pendingIntent, lastUsedTimeMillis
- ?: 0L, null, false)
+ val credentialEntry = CredentialEntry(
+ credentialType, credentialTypeDisplayName, userName,
+ userDisplayName, pendingIntent, lastUsedTimeMillis
+ ?: 0L, null, false
+ )
return Entry(
- key,
- subkey,
- CredentialEntry.toSlice(credentialEntry),
- Intent()
- )
- }
-
- private fun newCreateEntry(
- key: String,
- subkey: String,
- providerDisplayName: String,
- passwordCount: Int,
- passkeyCount: Int,
- totalCredentialCount: Int,
- lastUsedTimeMillis: Long,
- footerDescription: String?,
- ): Entry {
- val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
- .setPackage("com.androidauth.androidvault")
- intent.putExtra("provider_extra_sample", "testprovider")
- val pendingIntent = PendingIntent.getActivity(context, 1,
- intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- or PendingIntent.FLAG_ONE_SHOT))
- val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
- android.service.credentials.CallingAppInfo(
- context.applicationInfo.packageName, SigningInfo()),
- TYPE_PASSWORD_CREDENTIAL,
- toCredentialDataBundle("beckett-bakert@gmail.com", "password123")
- )
- val fillInIntent = Intent().putExtra(
- CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
- createPasswordRequest)
-
- val createEntry = CreateEntry(
- providerDisplayName, pendingIntent,
- null, lastUsedTimeMillis,
- listOf(
- CredentialCountInformation.createPasswordCountInformation(passwordCount),
- CredentialCountInformation.createPublicKeyCountInformation(passkeyCount),
- ), footerDescription)
- return Entry(
- key,
- subkey,
- CreateEntry.toSlice(createEntry),
- fillInIntent
+ key,
+ subkey,
+ CredentialEntry.toSlice(credentialEntry),
+ Intent()
)
}
- private fun newRemoteEntry(
- key: String,
- subkey: String,
- ): Entry {
- return Entry(
- key,
- subkey,
- Slice.Builder(
- Uri.EMPTY, SliceSpec("type", 1)
- ).build()
- )
- }
-
- private fun testCreatePasskeyRequestInfo(): RequestInfo {
- val request = CreatePublicKeyCredentialRequest("{\"extensions\": {\n" +
- " \"webauthn.loc\": true\n" +
- " },\n" +
- " \"attestation\": \"direct\",\n" +
- " \"challenge\": \"-rSQHXSQUdaK1N-La5bE-JPt6EVAW4SxX1K_tXhZ_Gk\",\n" +
- " \"user\": {\n" +
- " \"displayName\": \"testName\",\n" +
- " \"name\": \"credManTesting@gmail.com\",\n" +
- " \"id\": \"eD4o2KoXLpgegAtnM5cDhhUPvvk2\"\n" +
- " },\n" +
- " \"excludeCredentials\": [],\n" +
- " \"rp\": {\n" +
- " \"name\": \"Address Book\",\n" +
- " \"id\": \"addressbook-c7876.uc.r.appspot.com\"\n" +
- " },\n" +
- " \"timeout\": 60000,\n" +
- " \"pubKeyCredParams\": [\n" +
- " {\n" +
- " \"type\": \"public-key\",\n" +
- " \"alg\": -7\n" +
- " },\n" +
- " {\n" +
- " \"type\": \"public-key\",\n" +
- " \"alg\": -257\n" +
- " },\n" +
- " {\n" +
- " \"type\": \"public-key\",\n" +
- " \"alg\": -37\n" +
- " }\n" +
- " ],\n" +
- " \"authenticatorSelection\": {\n" +
- " \"residentKey\": \"required\",\n" +
- " \"requireResidentKey\": true\n" +
- " }}")
- val credentialData = request.credentialData
- return RequestInfo.newCreateRequestInfo(
- Binder(),
- CreateCredentialRequest(
- TYPE_PUBLIC_KEY_CREDENTIAL,
- credentialData,
- // TODO: populate with actual data
- /*candidateQueryData=*/ Bundle(),
- /*isSystemProviderRequired=*/ false
- ),
- "com.google.android.youtube"
- )
- }
-
- private fun testCreatePasswordRequestInfo(): RequestInfo {
- val data = toCredentialDataBundle("beckett-bakert@gmail.com", "password123")
- return RequestInfo.newCreateRequestInfo(
- Binder(),
- CreateCredentialRequest(
- TYPE_PASSWORD_CREDENTIAL,
- data,
- // TODO: populate with actual data
- /*candidateQueryData=*/ Bundle(),
- /*isSystemProviderRequired=*/ false
- ),
- "com.google.android.youtube"
- )
- }
-
- private fun testCreateOtherCredentialRequestInfo(): RequestInfo {
- val data = Bundle()
- return RequestInfo.newCreateRequestInfo(
- Binder(),
- CreateCredentialRequest(
- "other-sign-ins",
- data,
- /*candidateQueryData=*/ Bundle(),
- /*isSystemProviderRequired=*/ false
- ),
- "com.google.android.youtube"
- )
- }
-
- private fun testGetRequestInfo(): RequestInfo {
- return RequestInfo.newGetRequestInfo(
- Binder(),
- GetCredentialRequest.Builder(
- Bundle()
- )
- .addGetCredentialOption(
- GetCredentialOption(
- TYPE_PUBLIC_KEY_CREDENTIAL, Bundle(), Bundle(), /*isSystemProviderRequired=*/ false)
+ private fun newCreateEntry(
+ key: String,
+ subkey: String,
+ providerDisplayName: String,
+ passwordCount: Int,
+ passkeyCount: Int,
+ totalCredentialCount: Int,
+ lastUsedTimeMillis: Long,
+ footerDescription: String?,
+ ): Entry {
+ val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+ .setPackage("com.androidauth.androidvault")
+ intent.putExtra("provider_extra_sample", "testprovider")
+ val pendingIntent = PendingIntent.getActivity(
+ context, 1,
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ or PendingIntent.FLAG_ONE_SHOT)
)
- .build(),
- "com.google.android.youtube"
- )
- }
+ val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
+ android.service.credentials.CallingAppInfo(
+ context.applicationInfo.packageName, SigningInfo()
+ ),
+ TYPE_PASSWORD_CREDENTIAL,
+ toCredentialDataBundle("beckett-bakert@gmail.com", "password123")
+ )
+ val fillInIntent = Intent().putExtra(
+ CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
+ createPasswordRequest
+ )
+
+ val createEntry = CreateEntry(
+ providerDisplayName, pendingIntent,
+ null, lastUsedTimeMillis,
+ listOf(
+ CredentialCountInformation.createPasswordCountInformation(passwordCount),
+ CredentialCountInformation.createPublicKeyCountInformation(passkeyCount),
+ ), footerDescription
+ )
+ return Entry(
+ key,
+ subkey,
+ CreateEntry.toSlice(createEntry),
+ fillInIntent
+ )
+ }
+
+ private fun newRemoteEntry(
+ key: String,
+ subkey: String,
+ ): Entry {
+ return Entry(
+ key,
+ subkey,
+ Slice.Builder(
+ Uri.EMPTY, SliceSpec("type", 1)
+ ).build()
+ )
+ }
+
+ private fun testCreatePasskeyRequestInfo(): RequestInfo {
+ val request = CreatePublicKeyCredentialRequest(
+ "{\"extensions\": {\n" +
+ " \"webauthn.loc\": true\n" +
+ " },\n" +
+ " \"attestation\": \"direct\",\n" +
+ " \"challenge\":" +
+ " \"-rSQHXSQUdaK1N-La5bE-JPt6EVAW4SxX1K_tXhZ_Gk\",\n" +
+ " \"user\": {\n" +
+ " \"displayName\": \"testName\",\n" +
+ " \"name\": \"credManTesting@gmail.com\",\n" +
+ " \"id\": \"eD4o2KoXLpgegAtnM5cDhhUPvvk2\"\n" +
+ " },\n" +
+ " \"excludeCredentials\": [],\n" +
+ " \"rp\": {\n" +
+ " \"name\": \"Address Book\",\n" +
+ " \"id\": \"addressbook-c7876.uc.r.appspot.com\"\n" +
+ " },\n" +
+ " \"timeout\": 60000,\n" +
+ " \"pubKeyCredParams\": [\n" +
+ " {\n" +
+ " \"type\": \"public-key\",\n" +
+ " \"alg\": -7\n" +
+ " },\n" +
+ " {\n" +
+ " \"type\": \"public-key\",\n" +
+ " \"alg\": -257\n" +
+ " },\n" +
+ " {\n" +
+ " \"type\": \"public-key\",\n" +
+ " \"alg\": -37\n" +
+ " }\n" +
+ " ],\n" +
+ " \"authenticatorSelection\": {\n" +
+ " \"residentKey\": \"required\",\n" +
+ " \"requireResidentKey\": true\n" +
+ " }}"
+ )
+ val credentialData = request.credentialData
+ return RequestInfo.newCreateRequestInfo(
+ Binder(),
+ CreateCredentialRequest(
+ TYPE_PUBLIC_KEY_CREDENTIAL,
+ credentialData,
+ // TODO: populate with actual data
+ /*candidateQueryData=*/ Bundle(),
+ /*isSystemProviderRequired=*/ false
+ ),
+ "com.google.android.youtube"
+ )
+ }
+
+ private fun testCreatePasswordRequestInfo(): RequestInfo {
+ val data = toCredentialDataBundle("beckett-bakert@gmail.com", "password123")
+ return RequestInfo.newCreateRequestInfo(
+ Binder(),
+ CreateCredentialRequest(
+ TYPE_PASSWORD_CREDENTIAL,
+ data,
+ // TODO: populate with actual data
+ /*candidateQueryData=*/ Bundle(),
+ /*isSystemProviderRequired=*/ false
+ ),
+ "com.google.android.youtube"
+ )
+ }
+
+ private fun testCreateOtherCredentialRequestInfo(): RequestInfo {
+ val data = Bundle()
+ return RequestInfo.newCreateRequestInfo(
+ Binder(),
+ CreateCredentialRequest(
+ "other-sign-ins",
+ data,
+ /*candidateQueryData=*/ Bundle(),
+ /*isSystemProviderRequired=*/ false
+ ),
+ "com.google.android.youtube"
+ )
+ }
+
+ private fun testGetRequestInfo(): RequestInfo {
+ return RequestInfo.newGetRequestInfo(
+ Binder(),
+ GetCredentialRequest.Builder(
+ Bundle()
+ )
+ .addGetCredentialOption(
+ GetCredentialOption(
+ TYPE_PUBLIC_KEY_CREDENTIAL,
+ Bundle(),
+ Bundle(), /*isSystemProviderRequired=*/
+ false
+ )
+ )
+ .build(),
+ "com.google.android.youtube"
+ )
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 0620f9a..3b9c02a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -17,6 +17,7 @@
package com.android.credentialmanager
import android.content.Intent
+import android.credentials.ui.RequestInfo
import android.os.Bundle
import android.provider.Settings
import android.util.Log
@@ -26,89 +27,119 @@
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewmodel.compose.viewModel
-import com.android.credentialmanager.common.DialogType
-import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.Constants
+import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
-import com.android.credentialmanager.common.ResultState
import com.android.credentialmanager.createflow.CreateCredentialScreen
import com.android.credentialmanager.createflow.CreateCredentialViewModel
import com.android.credentialmanager.getflow.GetCredentialScreen
import com.android.credentialmanager.getflow.GetCredentialViewModel
import com.android.credentialmanager.ui.theme.CredentialSelectorTheme
-import kotlinx.coroutines.launch
@ExperimentalMaterialApi
class CredentialSelectorActivity : ComponentActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val credManRepo = CredentialManagerRepo(this, intent)
- UserConfigRepo.setup(this)
- val requestInfo = credManRepo.requestInfo
- setContent {
- CredentialSelectorTheme {
- CredentialManagerBottomSheet(DialogType.toDialogType(requestInfo.type), credManRepo)
- }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val credManRepo = CredentialManagerRepo(this, intent)
+ UserConfigRepo.setup(this)
+ try {
+ setContent {
+ CredentialSelectorTheme {
+ CredentialManagerBottomSheet(credManRepo.requestInfo.type, credManRepo)
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(Constants.LOG_TAG, "Failed to show the credential selector", e)
+ reportInstantiationErrorAndFinishActivity(credManRepo)
+ }
}
- }
- @ExperimentalMaterialApi
- @Composable
- fun CredentialManagerBottomSheet(dialogType: DialogType, credManRepo: CredentialManagerRepo) {
- val providerActivityResult = remember { mutableStateOf<ProviderActivityResult?>(null) }
- val launcher = rememberLauncherForActivityResult(
- ActivityResultContracts.StartIntentSenderForResult()
- ) {
- providerActivityResult.value = ProviderActivityResult(it.resultCode, it.data)
+ @ExperimentalMaterialApi
+ @Composable
+ fun CredentialManagerBottomSheet(requestType: String, credManRepo: CredentialManagerRepo) {
+ val providerActivityResult = remember { mutableStateOf<ProviderActivityResult?>(null) }
+ val launcher = rememberLauncherForActivityResult(
+ ActivityResultContracts.StartIntentSenderForResult()
+ ) {
+ providerActivityResult.value = ProviderActivityResult(it.resultCode, it.data)
+ }
+ when (requestType) {
+ RequestInfo.TYPE_CREATE -> {
+ val viewModel: CreateCredentialViewModel = viewModel {
+ val vm = CreateCredentialViewModel.newInstance(
+ credManRepo = credManRepo,
+ providerEnableListUiState =
+ credManRepo.getCreateProviderEnableListInitialUiState(),
+ providerDisableListUiState =
+ credManRepo.getCreateProviderDisableListInitialUiState(),
+ requestDisplayInfoUiState =
+ credManRepo.getCreateRequestDisplayInfoInitialUiState()
+ )
+ if (vm == null) {
+ // Input parsing failed. Close the activity.
+ reportInstantiationErrorAndFinishActivity(credManRepo)
+ throw IllegalStateException()
+ } else {
+ vm
+ }
+ }
+ LaunchedEffect(viewModel.uiState.dialogState) {
+ handleDialogState(viewModel.uiState.dialogState)
+ }
+ providerActivityResult.value?.let {
+ viewModel.onProviderActivityResult(it)
+ providerActivityResult.value = null
+ }
+ CreateCredentialScreen(
+ viewModel = viewModel,
+ providerActivityLauncher = launcher
+ )
+ }
+ RequestInfo.TYPE_GET -> {
+ val viewModel: GetCredentialViewModel = viewModel {
+ val initialUiState = credManRepo.getCredentialInitialUiState()
+ if (initialUiState == null) {
+ // Input parsing failed. Close the activity.
+ reportInstantiationErrorAndFinishActivity(credManRepo)
+ throw IllegalStateException()
+ } else {
+ GetCredentialViewModel(credManRepo, initialUiState)
+ }
+ }
+ LaunchedEffect(viewModel.uiState.dialogState) {
+ handleDialogState(viewModel.uiState.dialogState)
+ }
+ providerActivityResult.value?.let {
+ viewModel.onProviderActivityResult(it)
+ providerActivityResult.value = null
+ }
+ GetCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
+ }
+ else -> {
+ Log.d(Constants.LOG_TAG, "Unknown type, not rendering any UI")
+ reportInstantiationErrorAndFinishActivity(credManRepo)
+ }
+ }
}
- when (dialogType) {
- DialogType.CREATE_PASSKEY -> {
- val viewModel: CreateCredentialViewModel = viewModel{
- CreateCredentialViewModel(credManRepo)
- }
- lifecycleScope.launch {
- viewModel.observeDialogResult().collect{ dialogResult ->
- onCancel(dialogResult)
- }
- }
- providerActivityResult.value?.let {
- viewModel.onProviderActivityResult(it)
- providerActivityResult.value = null
- }
- CreateCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
- }
- DialogType.GET_CREDENTIALS -> {
- val viewModel: GetCredentialViewModel = viewModel{
- GetCredentialViewModel(credManRepo)
- }
- lifecycleScope.launch {
- viewModel.observeDialogResult().collect{ dialogResult ->
- onCancel(dialogResult)
- }
- }
- providerActivityResult.value?.let {
- viewModel.onProviderActivityResult(it)
- providerActivityResult.value = null
- }
- GetCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
- }
- else -> {
- Log.w("AccountSelector", "Unknown type, not rendering any UI")
- this.finish()
- }
- }
- }
- private fun onCancel(dialogResut: DialogResult) {
- if (dialogResut.resultState == ResultState
- .COMPLETE || dialogResut.resultState == ResultState.NORMAL_CANCELED) {
- this@CredentialSelectorActivity.finish()
- } else if (dialogResut.resultState == ResultState.LAUNCH_SETTING_CANCELED) {
- this@CredentialSelectorActivity.startActivity(Intent(Settings.ACTION_SYNC_SETTINGS))
- this@CredentialSelectorActivity.finish()
+ private fun reportInstantiationErrorAndFinishActivity(credManRepo: CredentialManagerRepo) {
+ Log.w(Constants.LOG_TAG, "Finishing the activity due to instantiation failure.")
+ credManRepo.onParsingFailureCancel()
+ this@CredentialSelectorActivity.finish()
}
- }
+
+ private fun handleDialogState(dialogState: DialogState) {
+ if (dialogState == DialogState.COMPLETE) {
+ Log.d(Constants.LOG_TAG, "Received signal to finish the activity.")
+ this@CredentialSelectorActivity.finish()
+ } else if (dialogState == DialogState.CANCELED_FOR_SETTINGS) {
+ Log.d(Constants.LOG_TAG, "Received signal to finish the activity and launch settings.")
+ this@CredentialSelectorActivity.startActivity(Intent(Settings.ACTION_SYNC_SETTINGS))
+ this@CredentialSelectorActivity.finish()
+ }
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index d9e4dc8..3f705d6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -19,21 +19,23 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
-import android.credentials.ui.Entry
-import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.DisabledProviderData
+import android.credentials.ui.Entry
+import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.RequestInfo
import android.graphics.drawable.Drawable
import android.text.TextUtils
+import android.util.Log
+import com.android.credentialmanager.common.Constants
+import com.android.credentialmanager.createflow.ActiveEntry
+import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.CreateOptionInfo
+import com.android.credentialmanager.createflow.CreateScreenState
+import com.android.credentialmanager.createflow.DisabledProviderInfo
+import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RemoteInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
-import com.android.credentialmanager.createflow.EnabledProviderInfo
-import com.android.credentialmanager.createflow.CreateScreenState
-import com.android.credentialmanager.createflow.ActiveEntry
-import com.android.credentialmanager.createflow.DisabledProviderInfo
-import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.getflow.ActionEntryInfo
import com.android.credentialmanager.getflow.AuthenticationEntryInfo
import com.android.credentialmanager.getflow.CredentialEntryInfo
@@ -45,416 +47,480 @@
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
import com.android.credentialmanager.jetpack.provider.Action
import com.android.credentialmanager.jetpack.provider.AuthenticationAction
+import com.android.credentialmanager.jetpack.provider.CreateEntry
import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
import com.android.credentialmanager.jetpack.provider.CredentialEntry
-import com.android.credentialmanager.jetpack.provider.CreateEntry
import org.json.JSONObject
+private fun getAppLabel(
+ pm: PackageManager,
+ appPackageName: String
+): String? {
+ return try {
+ val pkgInfo = pm.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0))
+ pkgInfo.applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(Constants.LOG_TAG, "Caller app not found", e)
+ null
+ }
+}
+
+private fun getServiceLabelAndIcon(
+ pm: PackageManager,
+ providerFlattenedComponentName: String
+): Pair<String, Drawable>? {
+ var providerLabel: String? = null
+ var providerIcon: Drawable? = null
+ val component = ComponentName.unflattenFromString(providerFlattenedComponentName)
+ if (component == null) {
+ // Test data has only package name not component name.
+ // TODO: remove once test data is removed
+ try {
+ val pkgInfo = pm.getPackageInfo(
+ providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ providerLabel =
+ pkgInfo.applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = pkgInfo.applicationInfo.loadIcon(pm)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(Constants.LOG_TAG, "Provider info not found", e)
+ }
+ } else {
+ try {
+ val si = pm.getServiceInfo(component, PackageManager.ComponentInfoFlags.of(0))
+ providerLabel = si.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = si.loadIcon(pm)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(Constants.LOG_TAG, "Provider info not found", e)
+ }
+ }
+ return if (providerLabel == null || providerIcon == null) {
+ Log.d(
+ Constants.LOG_TAG,
+ "Failed to load provider label/icon for provider $providerFlattenedComponentName"
+ )
+ null
+ } else {
+ Pair(providerLabel, providerIcon)
+ }
+}
+
/** Utility functions for converting CredentialManager data structures to or from UI formats. */
class GetFlowUtils {
- companion object {
-
- fun toProviderList(
+ companion object {
+ // Returns the list (potentially empty) of enabled provider.
+ fun toProviderList(
providerDataList: List<GetCredentialProviderData>,
context: Context,
- ): List<ProviderInfo> {
- val packageManager = context.packageManager
- return providerDataList.map {
- val componentName = ComponentName.unflattenFromString(it.providerFlattenedComponentName)
- var packageName = componentName?.packageName
- if (componentName == null) {
- // TODO: Remove once test data is fixed
- packageName = it.providerFlattenedComponentName
+ ): List<ProviderInfo> {
+ val providerList: MutableList<ProviderInfo> = mutableListOf()
+ providerDataList.forEach {
+ val providerLabelAndIcon = getServiceLabelAndIcon(
+ context.packageManager,
+ it.providerFlattenedComponentName
+ ) ?: return@forEach
+ val (providerLabel, providerIcon) = providerLabelAndIcon
+ providerList.add(
+ ProviderInfo(
+ id = it.providerFlattenedComponentName,
+ icon = providerIcon,
+ displayName = providerLabel,
+ credentialEntryList = getCredentialOptionInfoList(
+ it.providerFlattenedComponentName, it.credentialEntries, context
+ ),
+ authenticationEntry = getAuthenticationEntry(
+ it.providerFlattenedComponentName,
+ providerLabel,
+ providerIcon,
+ it.authenticationEntry
+ ),
+ remoteEntry = getRemoteEntry(
+ it.providerFlattenedComponentName,
+ it.remoteEntry
+ ),
+ actionEntryList = getActionEntryList(
+ it.providerFlattenedComponentName, it.actionChips, providerIcon
+ ),
+ )
+ )
+ }
+ return providerList
}
- val pkgInfo = packageManager
- .getPackageInfo(packageName!!,
- PackageManager.PackageInfoFlags.of(0))
- val providerDisplayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString()
- // TODO: decide what to do when failed to load a provider icon
- val providerIcon = pkgInfo.applicationInfo.loadIcon(packageManager)!!
- ProviderInfo(
- id = it.providerFlattenedComponentName,
- // TODO: decide what to do when failed to load a provider icon
- icon = providerIcon,
- displayName = providerDisplayName,
- credentialEntryList = getCredentialOptionInfoList(
- it.providerFlattenedComponentName, it.credentialEntries, context),
- authenticationEntry = getAuthenticationEntry(
- it.providerFlattenedComponentName,
- providerDisplayName,
- providerIcon,
- it.authenticationEntry),
- remoteEntry = getRemoteEntry(it.providerFlattenedComponentName, it.remoteEntry),
- actionEntryList = getActionEntryList(
- it.providerFlattenedComponentName, it.actionChips, providerIcon),
- )
- }
- }
-
- fun toRequestDisplayInfo(
+ fun toRequestDisplayInfo(
requestInfo: RequestInfo,
context: Context,
- ): com.android.credentialmanager.getflow.RequestDisplayInfo {
- val packageName = requestInfo.appPackageName
- val pkgInfo = context.packageManager.getPackageInfo(packageName,
- PackageManager.PackageInfoFlags.of(0))
- val appLabel = pkgInfo.applicationInfo.loadSafeLabel(context.packageManager, 0f,
- TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM)
- return com.android.credentialmanager.getflow.RequestDisplayInfo(
- appName = appLabel.toString()
- )
- }
+ ): com.android.credentialmanager.getflow.RequestDisplayInfo? {
+ return com.android.credentialmanager.getflow.RequestDisplayInfo(
+ appName = getAppLabel(context.packageManager, requestInfo.appPackageName)
+ ?: return null
+ )
+ }
- /* From service data structure to UI credential entry list representation. */
- private fun getCredentialOptionInfoList(
+ /* From service data structure to UI credential entry list representation. */
+ private fun getCredentialOptionInfoList(
providerId: String,
credentialEntries: List<Entry>,
context: Context,
- ): List<CredentialEntryInfo> {
- return credentialEntries.map {
- // TODO: handle NPE gracefully
- val credentialEntry = CredentialEntry.fromSlice(it.slice)!!
+ ): List<CredentialEntryInfo> {
+ return credentialEntries.map {
+ // TODO: handle NPE gracefully
+ val credentialEntry = CredentialEntry.fromSlice(it.slice)!!
- // Consider directly move the UI object into the class.
- return@map CredentialEntryInfo(
- providerId = providerId,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = credentialEntry.type.toString(),
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- // TODO: proper fallback
- icon = credentialEntry.icon?.loadDrawable(context),
- lastUsedTimeMillis = credentialEntry.lastUsedTimeMillis,
- )
- }
- }
+ // Consider directly move the UI object into the class.
+ return@map CredentialEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = credentialEntry.type,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ // TODO: proper fallback
+ icon = credentialEntry.icon?.loadDrawable(context),
+ lastUsedTimeMillis = credentialEntry.lastUsedTimeMillis,
+ )
+ }
+ }
- private fun getAuthenticationEntry(
+ private fun getAuthenticationEntry(
providerId: String,
providerDisplayName: String,
providerIcon: Drawable,
authEntry: Entry?,
- ): AuthenticationEntryInfo? {
- if (authEntry == null) {
- return null
- }
- val authStructuredEntry = AuthenticationAction.fromSlice(
- authEntry!!.slice)
- if (authStructuredEntry == null) {
- return null
- }
+ ): AuthenticationEntryInfo? {
+ if (authEntry == null) {
+ return null
+ }
+ val authStructuredEntry = AuthenticationAction.fromSlice(
+ authEntry!!.slice
+ )
+ if (authStructuredEntry == null) {
+ return null
+ }
- return AuthenticationEntryInfo(
- providerId = providerId,
- entryKey = authEntry.key,
- entrySubkey = authEntry.subkey,
- pendingIntent = authStructuredEntry.pendingIntent,
- fillInIntent = authEntry.frameworkExtrasIntent,
- title = providerDisplayName,
- icon = providerIcon,
- )
- }
+ return AuthenticationEntryInfo(
+ providerId = providerId,
+ entryKey = authEntry.key,
+ entrySubkey = authEntry.subkey,
+ pendingIntent = authStructuredEntry.pendingIntent,
+ fillInIntent = authEntry.frameworkExtrasIntent,
+ title = providerDisplayName,
+ icon = providerIcon,
+ )
+ }
- private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
- // TODO: should also call fromSlice after getting the official jetpack code.
- if (remoteEntry == null) {
- return null
- }
- return RemoteEntryInfo(
- providerId = providerId,
- entryKey = remoteEntry.key,
- entrySubkey = remoteEntry.subkey,
- pendingIntent = remoteEntry.pendingIntent,
- fillInIntent = remoteEntry.frameworkExtrasIntent,
- )
- }
+ private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
+ // TODO: should also call fromSlice after getting the official jetpack code.
+ if (remoteEntry == null) {
+ return null
+ }
+ return RemoteEntryInfo(
+ providerId = providerId,
+ entryKey = remoteEntry.key,
+ entrySubkey = remoteEntry.subkey,
+ pendingIntent = remoteEntry.pendingIntent,
+ fillInIntent = remoteEntry.frameworkExtrasIntent,
+ )
+ }
- private fun getActionEntryList(
+ private fun getActionEntryList(
providerId: String,
actionEntries: List<Entry>,
providerIcon: Drawable,
- ): List<ActionEntryInfo> {
- return actionEntries.map {
- // TODO: handle NPE gracefully
- val actionEntryUi = Action.fromSlice(it.slice)!!
+ ): List<ActionEntryInfo> {
+ return actionEntries.map {
+ // TODO: handle NPE gracefully
+ val actionEntryUi = Action.fromSlice(it.slice)!!
- return@map ActionEntryInfo(
- providerId = providerId,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = actionEntryUi.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- title = actionEntryUi.title.toString(),
- // TODO: gracefully fail
- icon = providerIcon,
- subTitle = actionEntryUi.subTitle?.toString(),
- )
- }
+ return@map ActionEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = actionEntryUi.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ title = actionEntryUi.title.toString(),
+ // TODO: gracefully fail
+ icon = providerIcon,
+ subTitle = actionEntryUi.subTitle?.toString(),
+ )
+ }
+ }
}
- }
}
class CreateFlowUtils {
- companion object {
-
- fun toEnabledProviderList(
+ companion object {
+ // Returns the list (potentially empty) of enabled provider.
+ fun toEnabledProviderList(
providerDataList: List<CreateCredentialProviderData>,
context: Context,
- ): List<EnabledProviderInfo> {
- // TODO: get from the actual service info
- val packageManager = context.packageManager
-
- return providerDataList.map {
- val componentName = ComponentName.unflattenFromString(it.providerFlattenedComponentName)
- var packageName = componentName?.packageName
- if (componentName == null) {
- // TODO: Remove once test data is fixed
- packageName = it.providerFlattenedComponentName
+ ): List<EnabledProviderInfo> {
+ val providerList: MutableList<EnabledProviderInfo> = mutableListOf()
+ providerDataList.forEach {
+ val providerLabelAndIcon = getServiceLabelAndIcon(
+ context.packageManager,
+ it.providerFlattenedComponentName
+ ) ?: return@forEach
+ val (providerLabel, providerIcon) = providerLabelAndIcon
+ providerList.add(EnabledProviderInfo(
+ id = it.providerFlattenedComponentName,
+ displayName = providerLabel,
+ icon = providerIcon,
+ createOptions = toCreationOptionInfoList(
+ it.providerFlattenedComponentName, it.saveEntries, context
+ ),
+ remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry),
+ ))
+ }
+ return providerList
}
- val pkgInfo = packageManager
- .getPackageInfo(packageName!!,
- PackageManager.PackageInfoFlags.of(0))
- EnabledProviderInfo(
- // TODO: decide what to do when failed to load a provider icon
- icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!,
- name = it.providerFlattenedComponentName,
- displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
- createOptions = toCreationOptionInfoList(
- it.providerFlattenedComponentName, it.saveEntries, context),
- remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry),
- )
- }
- }
-
- fun toDisabledProviderList(
+ // Returns the list (potentially empty) of disabled provider.
+ fun toDisabledProviderList(
providerDataList: List<DisabledProviderData>?,
context: Context,
- ): List<DisabledProviderInfo>? {
- // TODO: get from the actual service info
- val packageManager = context.packageManager
- return providerDataList?.map {
- val componentName = ComponentName.unflattenFromString(it.providerFlattenedComponentName)
- var packageName = componentName?.packageName
- if (componentName == null) {
- // TODO: Remove once test data is fixed
- packageName = it.providerFlattenedComponentName
+ ): List<DisabledProviderInfo> {
+ val providerList: MutableList<DisabledProviderInfo> = mutableListOf()
+ providerDataList?.forEach {
+ val providerLabelAndIcon = getServiceLabelAndIcon(
+ context.packageManager,
+ it.providerFlattenedComponentName
+ ) ?: return@forEach
+ val (providerLabel, providerIcon) = providerLabelAndIcon
+ providerList.add(DisabledProviderInfo(
+ icon = providerIcon,
+ id = it.providerFlattenedComponentName,
+ displayName = providerLabel,
+ ))
+ }
+ return providerList
}
- val pkgInfo = packageManager
- .getPackageInfo(packageName!!,
- PackageManager.PackageInfoFlags.of(0))
- DisabledProviderInfo(
- icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!,
- name = it.providerFlattenedComponentName,
- displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
- )
- }
- }
- fun toRequestDisplayInfo(
+ fun toRequestDisplayInfo(
requestInfo: RequestInfo,
context: Context,
- ): RequestDisplayInfo {
- val packageName = requestInfo.appPackageName
- val pkgInfo = context.packageManager.getPackageInfo(packageName,
- PackageManager.PackageInfoFlags.of(0))
- val appLabel = pkgInfo.applicationInfo.loadSafeLabel(context.packageManager, 0f,
- TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM)
- val createCredentialRequest = requestInfo.createCredentialRequest
- val createCredentialRequestJetpack = createCredentialRequest?.let {
- CreateCredentialRequest.createFrom(
- it.type, it.credentialData, it.candidateQueryData, it.isSystemProviderRequired()
- )
- }
- when (createCredentialRequestJetpack) {
- is CreatePasswordRequest -> {
- return RequestDisplayInfo(
- createCredentialRequestJetpack.id,
- createCredentialRequestJetpack.password,
- createCredentialRequestJetpack.type,
- appLabel.toString(),
- context.getDrawable(R.drawable.ic_password)!!
- )
+ ): RequestDisplayInfo? {
+ val appLabel = getAppLabel(context.packageManager, requestInfo.appPackageName)
+ ?: return null
+ val createCredentialRequest = requestInfo.createCredentialRequest
+ val createCredentialRequestJetpack = createCredentialRequest?.let {
+ CreateCredentialRequest.createFrom(
+ it.type, it.credentialData, it.candidateQueryData, it.isSystemProviderRequired
+ )
+ }
+ when (createCredentialRequestJetpack) {
+ is CreatePasswordRequest -> {
+ return RequestDisplayInfo(
+ createCredentialRequestJetpack.id,
+ createCredentialRequestJetpack.password,
+ createCredentialRequestJetpack.type,
+ appLabel,
+ context.getDrawable(R.drawable.ic_password)!!
+ )
+ }
+ is CreatePublicKeyCredentialRequest -> {
+ val requestJson = createCredentialRequestJetpack.requestJson
+ val json = JSONObject(requestJson)
+ var name = ""
+ var displayName = ""
+ if (json.has("user")) {
+ val user: JSONObject = json.getJSONObject("user")
+ name = user.getString("name")
+ displayName = user.getString("displayName")
+ }
+ return RequestDisplayInfo(
+ name,
+ displayName,
+ createCredentialRequestJetpack.type,
+ appLabel,
+ context.getDrawable(R.drawable.ic_passkey)!!
+ )
+ }
+ // TODO: correctly parsing for other sign-ins
+ else -> {
+ return RequestDisplayInfo(
+ "beckett-bakert@gmail.com",
+ "Elisa Beckett",
+ "other-sign-ins",
+ appLabel.toString(),
+ context.getDrawable(R.drawable.ic_other_sign_in)!!
+ )
+ }
+ }
}
- is CreatePublicKeyCredentialRequest -> {
- val requestJson = createCredentialRequestJetpack.requestJson
- val json = JSONObject(requestJson)
- var name = ""
- var displayName = ""
- if (json.has("user")) {
- val user: JSONObject = json.getJSONObject("user")
- name = user.getString("name")
- displayName = user.getString("displayName")
- }
- return RequestDisplayInfo(
- name,
- displayName,
- createCredentialRequestJetpack.type,
- appLabel.toString(),
- context.getDrawable(R.drawable.ic_passkey)!!)
- }
- // TODO: correctly parsing for other sign-ins
- else -> {
- return RequestDisplayInfo(
- "beckett-bakert@gmail.com",
- "Elisa Beckett",
- "other-sign-ins",
- appLabel.toString(),
- context.getDrawable(R.drawable.ic_other_sign_in)!!)
- }
- }
- }
- fun toCreateCredentialUiState(
+ fun toCreateCredentialUiState(
enabledProviders: List<EnabledProviderInfo>,
disabledProviders: List<DisabledProviderInfo>?,
defaultProviderId: String?,
requestDisplayInfo: RequestDisplayInfo,
isOnPasskeyIntroStateAlready: Boolean,
isPasskeyFirstUse: Boolean,
- ): CreateCredentialUiState {
- var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
- var remoteEntry: RemoteInfo? = null
- var defaultProvider: EnabledProviderInfo? = null
- var createOptionsPairs:
- MutableList<Pair<CreateOptionInfo, EnabledProviderInfo>> = mutableListOf()
- enabledProviders.forEach {
- enabledProvider ->
- if (defaultProviderId != null) {
- if (enabledProvider.id == defaultProviderId) {
- defaultProvider = enabledProvider
- }
+ ): CreateCredentialUiState? {
+ var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
+ var remoteEntry: RemoteInfo? = null
+ var defaultProvider: EnabledProviderInfo? = null
+ var createOptionsPairs:
+ MutableList<Pair<CreateOptionInfo, EnabledProviderInfo>> = mutableListOf()
+ enabledProviders.forEach { enabledProvider ->
+ if (defaultProviderId != null) {
+ if (enabledProvider.id == defaultProviderId) {
+ defaultProvider = enabledProvider
+ }
+ }
+ if (enabledProvider.createOptions.isNotEmpty()) {
+ lastSeenProviderWithNonEmptyCreateOptions = enabledProvider
+ enabledProvider.createOptions.forEach {
+ createOptionsPairs.add(Pair(it, enabledProvider))
+ }
+ }
+ if (enabledProvider.remoteEntry != null) {
+ remoteEntry = enabledProvider.remoteEntry!!
+ }
+ }
+ val initialScreenState = toCreateScreenState(
+ /*createOptionSize=*/createOptionsPairs.size,
+ /*isOnPasskeyIntroStateAlready=*/isOnPasskeyIntroStateAlready,
+ /*requestDisplayInfo=*/requestDisplayInfo,
+ /*defaultProvider=*/defaultProvider, /*remoteEntry=*/remoteEntry,
+ /*isPasskeyFirstUse=*/isPasskeyFirstUse
+ )
+ if (initialScreenState == null) {
+ return null
+ }
+ return CreateCredentialUiState(
+ enabledProviders = enabledProviders,
+ disabledProviders = disabledProviders,
+ currentScreenState = initialScreenState,
+ requestDisplayInfo = requestDisplayInfo,
+ sortedCreateOptionsPairs = createOptionsPairs.sortedWith(
+ compareByDescending { it.first.lastUsedTimeMillis }
+ ),
+ hasDefaultProvider = defaultProvider != null,
+ activeEntry = toActiveEntry(
+ /*defaultProvider=*/defaultProvider,
+ /*createOptionSize=*/createOptionsPairs.size,
+ /*lastSeenProviderWithNonEmptyCreateOptions=*/
+ lastSeenProviderWithNonEmptyCreateOptions,
+ /*remoteEntry=*/remoteEntry
+ ),
+ )
}
- if (enabledProvider.createOptions.isNotEmpty()) {
- lastSeenProviderWithNonEmptyCreateOptions = enabledProvider
- enabledProvider.createOptions.forEach {
- createOptionsPairs.add(Pair(it, enabledProvider))
- }
- }
- if (enabledProvider.remoteEntry != null) {
- remoteEntry = enabledProvider.remoteEntry!!
- }
- }
- return CreateCredentialUiState(
- enabledProviders = enabledProviders,
- disabledProviders = disabledProviders,
- toCreateScreenState(
- /*createOptionSize=*/createOptionsPairs.size,
- /*isOnPasskeyIntroStateAlready=*/isOnPasskeyIntroStateAlready,
- /*requestDisplayInfo=*/requestDisplayInfo,
- /*defaultProvider=*/defaultProvider, /*remoteEntry=*/remoteEntry,
- /*isPasskeyFirstUse=*/isPasskeyFirstUse),
- requestDisplayInfo,
- createOptionsPairs.sortedWith(compareByDescending{ it.first.lastUsedTimeMillis }),
- defaultProvider != null,
- toActiveEntry(
- /*defaultProvider=*/defaultProvider,
- /*createOptionSize=*/createOptionsPairs.size,
- /*lastSeenProviderWithNonEmptyCreateOptions=*/
- lastSeenProviderWithNonEmptyCreateOptions,
- /*remoteEntry=*/remoteEntry),
- )
- }
- private fun toCreateScreenState(
+ private fun toCreateScreenState(
createOptionSize: Int,
isOnPasskeyIntroStateAlready: Boolean,
requestDisplayInfo: RequestDisplayInfo,
defaultProvider: EnabledProviderInfo?,
remoteEntry: RemoteInfo?,
isPasskeyFirstUse: Boolean,
- ): CreateScreenState {
- return if (
- isPasskeyFirstUse && requestDisplayInfo
- .type == TYPE_PUBLIC_KEY_CREDENTIAL && !isOnPasskeyIntroStateAlready) {
- CreateScreenState.PASSKEY_INTRO
- } else if (
- (defaultProvider == null || defaultProvider.createOptions.isEmpty()
- ) && createOptionSize > 1) {
- CreateScreenState.PROVIDER_SELECTION
- } else if (
- ((defaultProvider == null || defaultProvider.createOptions.isEmpty()
- ) && createOptionSize == 1) || (
- defaultProvider != null && defaultProvider.createOptions.isNotEmpty())) {
- CreateScreenState.CREATION_OPTION_SELECTION
- } else if (createOptionSize == 0 && remoteEntry != null) {
- CreateScreenState.EXTERNAL_ONLY_SELECTION
- } else {
- // TODO: properly handle error and gracefully finish itself
- throw java.lang.IllegalStateException("Empty provider list.")
- }
- }
+ ): CreateScreenState? {
+ return if (isPasskeyFirstUse && requestDisplayInfo.type ==
+ TYPE_PUBLIC_KEY_CREDENTIAL && !isOnPasskeyIntroStateAlready) {
+ CreateScreenState.PASSKEY_INTRO
+ } else if ((defaultProvider == null || defaultProvider.createOptions.isEmpty()) &&
+ createOptionSize > 1) {
+ CreateScreenState.PROVIDER_SELECTION
+ } else if (((defaultProvider == null || defaultProvider.createOptions.isEmpty()) &&
+ createOptionSize == 1) || (defaultProvider != null &&
+ defaultProvider.createOptions.isNotEmpty())) {
+ CreateScreenState.CREATION_OPTION_SELECTION
+ } else if (createOptionSize == 0 && remoteEntry != null) {
+ CreateScreenState.EXTERNAL_ONLY_SELECTION
+ } else {
+ Log.d(
+ Constants.LOG_TAG,
+ "Unexpected failure: the screen state failed to instantiate" +
+ " because the provider list is empty."
+ )
+ null
+ }
+ }
- private fun toActiveEntry(
+ private fun toActiveEntry(
defaultProvider: EnabledProviderInfo?,
createOptionSize: Int,
lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo?,
remoteEntry: RemoteInfo?,
- ): ActiveEntry? {
- return if (
- defaultProvider != null && defaultProvider.createOptions.isEmpty() &&
- remoteEntry != null) {
- ActiveEntry(defaultProvider, remoteEntry)
- } else if (
- defaultProvider != null && defaultProvider.createOptions.isNotEmpty()
- ) {
- ActiveEntry(defaultProvider, defaultProvider.createOptions.first())
- } else if (createOptionSize == 1) {
- ActiveEntry(lastSeenProviderWithNonEmptyCreateOptions!!,
- lastSeenProviderWithNonEmptyCreateOptions.createOptions.first())
- } else null
- }
+ ): ActiveEntry? {
+ return if (
+ defaultProvider != null && defaultProvider.createOptions.isEmpty() &&
+ remoteEntry != null
+ ) {
+ ActiveEntry(defaultProvider, remoteEntry)
+ } else if (
+ defaultProvider != null && defaultProvider.createOptions.isNotEmpty()
+ ) {
+ ActiveEntry(defaultProvider, defaultProvider.createOptions.first())
+ } else if (createOptionSize == 1) {
+ ActiveEntry(
+ lastSeenProviderWithNonEmptyCreateOptions!!,
+ lastSeenProviderWithNonEmptyCreateOptions.createOptions.first()
+ )
+ } else null
+ }
- private fun toCreationOptionInfoList(
+ private fun toCreationOptionInfoList(
providerId: String,
creationEntries: List<Entry>,
context: Context,
- ): List<CreateOptionInfo> {
- return creationEntries.map {
- // TODO: handle NPE gracefully
- val createEntry = CreateEntry.fromSlice(it.slice)!!
+ ): List<CreateOptionInfo> {
+ return creationEntries.map {
+ // TODO: handle NPE gracefully
+ val createEntry = CreateEntry.fromSlice(it.slice)!!
- return@map CreateOptionInfo(
- // TODO: remove fallbacks
- providerId = providerId,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = createEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- userProviderDisplayName = createEntry.accountName.toString(),
- profileIcon = createEntry.icon?.loadDrawable(context),
- passwordCount = CredentialCountInformation.getPasswordCount(
- createEntry.credentialCountInformationList) ?: 0,
- passkeyCount = CredentialCountInformation.getPasskeyCount(
- createEntry.credentialCountInformationList) ?: 0,
- totalCredentialCount = CredentialCountInformation.getTotalCount(
- createEntry.credentialCountInformationList) ?: 0,
- lastUsedTimeMillis = createEntry.lastUsedTimeMillis ?: 0,
- footerDescription = createEntry.footerDescription?.toString()
- )
- }
- }
+ return@map CreateOptionInfo(
+ // TODO: remove fallbacks
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = createEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ userProviderDisplayName = createEntry.accountName.toString(),
+ profileIcon = createEntry.icon?.loadDrawable(context),
+ passwordCount = CredentialCountInformation.getPasswordCount(
+ createEntry.credentialCountInformationList
+ ) ?: 0,
+ passkeyCount = CredentialCountInformation.getPasskeyCount(
+ createEntry.credentialCountInformationList
+ ) ?: 0,
+ totalCredentialCount = CredentialCountInformation.getTotalCount(
+ createEntry.credentialCountInformationList
+ ) ?: 0,
+ lastUsedTimeMillis = createEntry.lastUsedTimeMillis ?: 0,
+ footerDescription = createEntry.footerDescription?.toString()
+ )
+ }
+ }
- private fun toRemoteInfo(
+ private fun toRemoteInfo(
providerId: String,
remoteEntry: Entry?,
- ): RemoteInfo? {
- // TODO: should also call fromSlice after getting the official jetpack code.
- return if (remoteEntry != null) {
- RemoteInfo(
- providerId = providerId,
- entryKey = remoteEntry.key,
- entrySubkey = remoteEntry.subkey,
- pendingIntent = remoteEntry.pendingIntent,
- fillInIntent = remoteEntry.frameworkExtrasIntent,
- )
- } else null
+ ): RemoteInfo? {
+ // TODO: should also call fromSlice after getting the official jetpack code.
+ return if (remoteEntry != null) {
+ RemoteInfo(
+ providerId = providerId,
+ entryKey = remoteEntry.key,
+ entrySubkey = remoteEntry.subkey,
+ pendingIntent = remoteEntry.pendingIntent,
+ fillInIntent = remoteEntry.frameworkExtrasIntent,
+ )
+ } else null
+ }
}
- }
}
diff --git a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
similarity index 65%
copy from core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
copy to packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
index 124a319..37e21a8 100644
--- a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,10 @@
* limitations under the License.
*/
-package android.credentials;
+package com.android.credentialmanager.common
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IRegisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
+class Constants {
+ companion object Constants {
+ const val LOG_TAG = "CredentialSelector"
+ }
}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
index 743f49b..6d07df7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
@@ -16,6 +16,12 @@
package com.android.credentialmanager.common
+enum class DialogState {
+ ACTIVE,
+ COMPLETE,
+ CANCELED_FOR_SETTINGS,
+}
+
enum class ResultState {
COMPLETE,
NORMAL_CANCELED,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt
index b5e9fd00..f40dc7e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt
@@ -19,16 +19,15 @@
import android.credentials.ui.RequestInfo
enum class DialogType {
- CREATE_PASSKEY,
+ CREATE_CREDENTIAL,
GET_CREDENTIALS,
- CREATE_PASSWORD,
UNKNOWN;
companion object {
fun toDialogType(value: String): DialogType {
return when (value) {
RequestInfo.TYPE_GET -> GET_CREDENTIALS
- RequestInfo.TYPE_CREATE -> CREATE_PASSKEY
+ RequestInfo.TYPE_CREATE -> CREATE_CREDENTIAL
else -> UNKNOWN
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityState.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityState.kt
new file mode 100644
index 0000000..b47d0e5
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityState.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager.common
+
+enum class ProviderActivityState {
+ /** No provider activity is active nor is any ready for launch, */
+ NOT_APPLICABLE,
+ /** Ready to launch the provider activity. */
+ READY_TO_LAUNCH,
+ /** The provider activity is launched and we are waiting for its result. We should hide our UI
+ * content when this happens. */
+ PENDING,
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 1e1c833..5e432b9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -37,6 +37,7 @@
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetLayout
import com.android.credentialmanager.common.material.ModalBottomSheetValue
import com.android.credentialmanager.common.material.rememberModalBottomSheetState
@@ -66,63 +67,83 @@
sheetState = state,
sheetContent = {
val uiState = viewModel.uiState
- if (!uiState.hidden) {
- when (uiState.currentScreenState) {
- CreateScreenState.PASSKEY_INTRO -> ConfirmationCard(
- onConfirm = viewModel::onConfirmIntro,
- onLearnMore = viewModel::onLearnMore,
- )
- CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
- requestDisplayInfo = uiState.requestDisplayInfo,
- enabledProviderList = uiState.enabledProviders,
- disabledProviderList = uiState.disabledProviders,
- sortedCreateOptionsPairs = uiState.sortedCreateOptionsPairs,
- onOptionSelected = viewModel::onEntrySelectedFromFirstUseScreen,
- onDisabledProvidersSelected = viewModel::onDisabledProvidersSelected,
- onMoreOptionsSelected = viewModel::onMoreOptionsSelectedOnProviderSelection,
- )
- CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
- requestDisplayInfo = uiState.requestDisplayInfo,
- enabledProviderList = uiState.enabledProviders,
- providerInfo = uiState.activeEntry?.activeProvider!!,
- createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo,
- onOptionSelected = viewModel::onEntrySelected,
- onConfirm = viewModel::onConfirmEntrySelected,
- onMoreOptionsSelected = viewModel::onMoreOptionsSelectedOnCreationSelection,
- )
- CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
- requestDisplayInfo = uiState.requestDisplayInfo,
- enabledProviderList = uiState.enabledProviders,
- disabledProviderList = uiState.disabledProviders,
- sortedCreateOptionsPairs = uiState.sortedCreateOptionsPairs,
- hasDefaultProvider = uiState.hasDefaultProvider,
- isFromProviderSelection = uiState.isFromProviderSelection!!,
- onBackProviderSelectionButtonSelected =
- viewModel::onBackProviderSelectionButtonSelected,
- onBackCreationSelectionButtonSelected =
- viewModel::onBackCreationSelectionButtonSelected,
- onOptionSelected = viewModel::onEntrySelectedFromMoreOptionScreen,
- onDisabledProvidersSelected = viewModel::onDisabledProvidersSelected,
- onRemoteEntrySelected = viewModel::onEntrySelected,
- )
- CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
- providerInfo = uiState.activeEntry?.activeProvider!!,
- onChangeDefaultSelected = viewModel::onChangeDefaultSelected,
- onUseOnceSelected = viewModel::onUseOnceSelected,
- )
- CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard(
- requestDisplayInfo = uiState.requestDisplayInfo,
- activeRemoteEntry = uiState.activeEntry?.activeEntryInfo!!,
- onOptionSelected = viewModel::onEntrySelected,
- onConfirm = viewModel::onConfirmEntrySelected,
- )
- CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO -> MoreAboutPasskeysIntroCard(
- onBackPasskeyIntroButtonSelected =
- viewModel::onBackPasskeyIntroButtonSelected,
- )
+ // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim
+ // background color even when the content should be hidden while waiting for
+ // results from the provider app.
+ when (uiState.providerActivityState) {
+ ProviderActivityState.NOT_APPLICABLE -> {
+ when (uiState.currentScreenState) {
+ CreateScreenState.PASSKEY_INTRO -> ConfirmationCard(
+ onConfirm = viewModel::onConfirmIntro,
+ onLearnMore = viewModel::onLearnMore,
+ )
+ CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
+ requestDisplayInfo = uiState.requestDisplayInfo,
+ enabledProviderList = uiState.enabledProviders,
+ disabledProviderList = uiState.disabledProviders,
+ sortedCreateOptionsPairs = uiState.sortedCreateOptionsPairs,
+ onOptionSelected = viewModel::onEntrySelectedFromFirstUseScreen,
+ onDisabledProvidersSelected =
+ viewModel::onDisabledProvidersSelected,
+ onMoreOptionsSelected =
+ viewModel::onMoreOptionsSelectedOnProviderSelection,
+ )
+ CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
+ requestDisplayInfo = uiState.requestDisplayInfo,
+ enabledProviderList = uiState.enabledProviders,
+ providerInfo = uiState.activeEntry?.activeProvider!!,
+ createOptionInfo =
+ uiState.activeEntry.activeEntryInfo as CreateOptionInfo,
+ onOptionSelected = viewModel::onEntrySelected,
+ onConfirm = viewModel::onConfirmEntrySelected,
+ onMoreOptionsSelected =
+ viewModel::onMoreOptionsSelectedOnCreationSelection,
+ )
+ CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
+ requestDisplayInfo = uiState.requestDisplayInfo,
+ enabledProviderList = uiState.enabledProviders,
+ disabledProviderList = uiState.disabledProviders,
+ sortedCreateOptionsPairs = uiState.sortedCreateOptionsPairs,
+ hasDefaultProvider = uiState.hasDefaultProvider,
+ isFromProviderSelection = uiState.isFromProviderSelection!!,
+ onBackProviderSelectionButtonSelected =
+ viewModel::onBackProviderSelectionButtonSelected,
+ onBackCreationSelectionButtonSelected =
+ viewModel::onBackCreationSelectionButtonSelected,
+ onOptionSelected =
+ viewModel::onEntrySelectedFromMoreOptionScreen,
+ onDisabledProvidersSelected =
+ viewModel::onDisabledProvidersSelected,
+ onRemoteEntrySelected = viewModel::onEntrySelected,
+ )
+ CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
+ providerInfo = uiState.activeEntry?.activeProvider!!,
+ onChangeDefaultSelected = viewModel::onChangeDefaultSelected,
+ onUseOnceSelected = viewModel::onUseOnceSelected,
+ )
+ CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard(
+ requestDisplayInfo = uiState.requestDisplayInfo,
+ activeRemoteEntry = uiState.activeEntry?.activeEntryInfo!!,
+ onOptionSelected = viewModel::onEntrySelected,
+ onConfirm = viewModel::onConfirmEntrySelected,
+ )
+ CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO ->
+ MoreAboutPasskeysIntroCard(
+ onBackPasskeyIntroButtonSelected =
+ viewModel::onBackPasskeyIntroButtonSelected,
+ )
+ }
}
- } else if (uiState.selectedEntry != null && !uiState.providerActivityPending) {
- viewModel.launchProviderUi(providerActivityLauncher)
+ ProviderActivityState.READY_TO_LAUNCH -> {
+ // Launch only once per providerActivityState change so that the provider
+ // UI will not be accidentally launched twice.
+ LaunchedEffect(uiState.providerActivityState) {
+ viewModel.launchProviderUi(providerActivityLauncher)
+ }
+ }
+ ProviderActivityState.PENDING -> {
+ // Hide our content when the provider activity is active.
+ }
}
},
scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f),
@@ -986,7 +1007,9 @@
)
// TODO: Update the subtitle once design is confirmed
TextSecondary(
- text = disabledProviders.joinToString(separator = " • ") { it.displayName },
+ text = disabledProviders.joinToString(separator = " • ") {
+ it.displayName
+ },
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
index a9b1d332..01318b1 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
@@ -25,243 +25,256 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
+import com.android.credentialmanager.common.Constants
import com.android.credentialmanager.CreateFlowUtils
import com.android.credentialmanager.CredentialManagerRepo
import com.android.credentialmanager.UserConfigRepo
-import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
-import com.android.credentialmanager.common.ResultState
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
+import com.android.credentialmanager.common.ProviderActivityState
-data class CreateCredentialUiState(
- val enabledProviders: List<EnabledProviderInfo>,
- val disabledProviders: List<DisabledProviderInfo>? = null,
- val currentScreenState: CreateScreenState,
- val requestDisplayInfo: RequestDisplayInfo,
- val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- // Should not change with the real time update of default provider, only determine whether we're
- // showing provider selection page at the beginning
- val hasDefaultProvider: Boolean,
- val activeEntry: ActiveEntry? = null,
- val selectedEntry: EntryInfo? = null,
- val hidden: Boolean = false,
- val providerActivityPending: Boolean = false,
- val isFromProviderSelection: Boolean? = null,
-)
class CreateCredentialViewModel(
- private val credManRepo: CredentialManagerRepo,
- userConfigRepo: UserConfigRepo = UserConfigRepo.getInstance(),
+ private val credManRepo: CredentialManagerRepo,
+ private val providerEnableListUiState: List<EnabledProviderInfo>,
+ private val providerDisableListUiState: List<DisabledProviderInfo>,
+ private val requestDisplayInfoUiState: RequestDisplayInfo,
+ userConfigRepo: UserConfigRepo = UserConfigRepo.getInstance(),
) : ViewModel() {
- var providerEnableListUiState = credManRepo.getCreateProviderEnableListInitialUiState()
- var providerDisableListUiState = credManRepo.getCreateProviderDisableListInitialUiState()
+ val defaultProviderId = userConfigRepo.getDefaultProviderId()
+ val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
- var requestDisplayInfoUiState = credManRepo.getCreateRequestDisplayInfoInitialUiState()
+ var uiState by mutableStateOf(
+ CreateFlowUtils.toCreateCredentialUiState(
+ providerEnableListUiState,
+ providerDisableListUiState,
+ defaultProviderId,
+ requestDisplayInfoUiState,
+ false,
+ isPasskeyFirstUse)!!)
+ private set
- var defaultProviderId = userConfigRepo.getDefaultProviderId()
-
- var isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
-
- var uiState by mutableStateOf(
- CreateFlowUtils.toCreateCredentialUiState(
- providerEnableListUiState,
- providerDisableListUiState,
- defaultProviderId,
- requestDisplayInfoUiState,
- false,
- isPasskeyFirstUse))
- private set
-
- val dialogResult: MutableSharedFlow<DialogResult> =
- MutableSharedFlow(replay = 0, extraBufferCapacity = 1,
- onBufferOverflow = BufferOverflow.DROP_OLDEST)
-
- fun observeDialogResult(): SharedFlow<DialogResult> {
- return dialogResult
- }
-
- fun onConfirmIntro() {
- uiState = CreateFlowUtils.toCreateCredentialUiState(
- providerEnableListUiState, providerDisableListUiState, defaultProviderId,
- requestDisplayInfoUiState, true, isPasskeyFirstUse)
- UserConfigRepo.getInstance().setIsPasskeyFirstUse(false)
- }
-
- fun getProviderInfoByName(providerId: String): EnabledProviderInfo {
- return uiState.enabledProviders.single {
- it.id == providerId
+ fun onConfirmIntro() {
+ val newUiState = CreateFlowUtils.toCreateCredentialUiState(
+ providerEnableListUiState, providerDisableListUiState, defaultProviderId,
+ requestDisplayInfoUiState, true, isPasskeyFirstUse)
+ if (newUiState == null) {
+ onInternalError()
+ return
+ }
+ uiState = newUiState
+ UserConfigRepo.getInstance().setIsPasskeyFirstUse(false)
}
- }
- fun onMoreOptionsSelectedOnProviderSelection() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION,
- isFromProviderSelection = true
- )
- }
+ fun getProviderInfoByName(providerId: String): EnabledProviderInfo {
+ return uiState.enabledProviders.single {
+ it.id == providerId
+ }
+ }
- fun onMoreOptionsSelectedOnCreationSelection() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION,
- isFromProviderSelection = false
- )
- }
+ fun onMoreOptionsSelectedOnProviderSelection() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION,
+ isFromProviderSelection = true
+ )
+ }
- fun onBackProviderSelectionButtonSelected() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.PROVIDER_SELECTION,
- )
- }
+ fun onMoreOptionsSelectedOnCreationSelection() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION,
+ isFromProviderSelection = false
+ )
+ }
- fun onBackCreationSelectionButtonSelected() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
- )
- }
+ fun onBackProviderSelectionButtonSelected() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.PROVIDER_SELECTION,
+ )
+ }
- fun onBackPasskeyIntroButtonSelected() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.PASSKEY_INTRO,
- )
- }
+ fun onBackCreationSelectionButtonSelected() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+ )
+ }
- fun onEntrySelectedFromMoreOptionScreen(activeEntry: ActiveEntry) {
- uiState = uiState.copy(
- currentScreenState = if (
- activeEntry.activeProvider.id == UserConfigRepo.getInstance().getDefaultProviderId()
- ) CreateScreenState.CREATION_OPTION_SELECTION else CreateScreenState.MORE_OPTIONS_ROW_INTRO,
- activeEntry = activeEntry
- )
- }
+ fun onBackPasskeyIntroButtonSelected() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.PASSKEY_INTRO,
+ )
+ }
- fun onEntrySelectedFromFirstUseScreen(activeEntry: ActiveEntry) {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
- activeEntry = activeEntry
- )
- val providerId = uiState.activeEntry?.activeProvider?.id
- onDefaultChanged(providerId)
- }
+ fun onEntrySelectedFromMoreOptionScreen(activeEntry: ActiveEntry) {
+ uiState = uiState.copy(
+ currentScreenState =
+ if (activeEntry.activeProvider.id ==
+ UserConfigRepo.getInstance().getDefaultProviderId())
+ CreateScreenState.CREATION_OPTION_SELECTION
+ else CreateScreenState.MORE_OPTIONS_ROW_INTRO,
+ activeEntry = activeEntry
+ )
+ }
- fun onDisabledProvidersSelected() {
- credManRepo.onSettingLaunchCancel()
- dialogResult.tryEmit(DialogResult(ResultState.LAUNCH_SETTING_CANCELED))
- }
+ fun onEntrySelectedFromFirstUseScreen(activeEntry: ActiveEntry) {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+ activeEntry = activeEntry
+ )
+ val providerId = uiState.activeEntry?.activeProvider?.id
+ onDefaultChanged(providerId)
+ }
- fun onCancel() {
- credManRepo.onUserCancel()
- dialogResult.tryEmit(DialogResult(ResultState.NORMAL_CANCELED))
- }
+ fun onDisabledProvidersSelected() {
+ credManRepo.onSettingLaunchCancel()
+ uiState = uiState.copy(dialogState = DialogState.CANCELED_FOR_SETTINGS)
+ }
- fun onLearnMore() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO,
- )
- }
+ // When the view model runs into unexpected illegal state, reports the error back and close
+ // the activity gracefully.
+ private fun onInternalError() {
+ Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state")
+ credManRepo.onParsingFailureCancel()
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ }
- fun onChangeDefaultSelected() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
- )
- val providerId = uiState.activeEntry?.activeProvider?.id
- onDefaultChanged(providerId)
- }
+ fun onCancel() {
+ credManRepo.onUserCancel()
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ }
- fun onUseOnceSelected() {
- uiState = uiState.copy(
- currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
- )
- }
+ fun onLearnMore() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO,
+ )
+ }
- fun onDefaultChanged(providerId: String?) {
- if (providerId != null) {
- Log.d(
- "Account Selector", "Default provider changed to: " +
+ fun onChangeDefaultSelected() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+ )
+ val providerId = uiState.activeEntry?.activeProvider?.id
+ onDefaultChanged(providerId)
+ }
+
+ fun onUseOnceSelected() {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+ )
+ }
+
+ fun onDefaultChanged(providerId: String?) {
+ if (providerId != null) {
+ Log.d(
+ Constants.LOG_TAG, "Default provider changed to: " +
" {provider=$providerId")
- UserConfigRepo.getInstance().setDefaultProvider(providerId)
- } else {
- Log.w("Account Selector", "Null provider is being changed")
+ UserConfigRepo.getInstance().setDefaultProvider(providerId)
+ } else {
+ Log.w(Constants.LOG_TAG, "Null provider is being changed")
+ }
}
- }
- fun onEntrySelected(selectedEntry: EntryInfo) {
- val providerId = selectedEntry.providerId
- val entryKey = selectedEntry.entryKey
- val entrySubkey = selectedEntry.entrySubkey
- Log.d(
- "Account Selector", "Option selected for entry: " +
- " {provider=$providerId, key=$entryKey, subkey=$entrySubkey")
- if (selectedEntry.pendingIntent != null) {
- uiState = uiState.copy(
- selectedEntry = selectedEntry,
- hidden = true,
- )
- } else {
- credManRepo.onOptionSelected(
- providerId,
- entryKey,
- entrySubkey
- )
- dialogResult.tryEmit(DialogResult(ResultState.COMPLETE))
+ fun onEntrySelected(selectedEntry: EntryInfo) {
+ val providerId = selectedEntry.providerId
+ val entryKey = selectedEntry.entryKey
+ val entrySubkey = selectedEntry.entrySubkey
+ Log.d(
+ Constants.LOG_TAG, "Option selected for entry: " +
+ " {provider=$providerId, key=$entryKey, subkey=$entrySubkey")
+ if (selectedEntry.pendingIntent != null) {
+ uiState = uiState.copy(
+ selectedEntry = selectedEntry,
+ providerActivityState = ProviderActivityState.READY_TO_LAUNCH,
+ )
+ } else {
+ credManRepo.onOptionSelected(
+ providerId,
+ entryKey,
+ entrySubkey
+ )
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ }
}
- }
- fun launchProviderUi(
- launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
- ) {
- val entry = uiState.selectedEntry
- if (entry != null && entry.pendingIntent != null) {
- uiState = uiState.copy(
- providerActivityPending = true,
- )
- val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
- .setFillInIntent(entry.fillInIntent).build()
- launcher.launch(intentSenderRequest)
- } else {
- Log.w("Account Selector", "No provider UI to launch")
+ fun launchProviderUi(
+ launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
+ ) {
+ val entry = uiState.selectedEntry
+ if (entry != null && entry.pendingIntent != null) {
+ uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
+ val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
+ .setFillInIntent(entry.fillInIntent).build()
+ launcher.launch(intentSenderRequest)
+ } else {
+ Log.d(Constants.LOG_TAG, "Unexpected: no provider UI to launch")
+ onInternalError()
+ }
}
- }
- fun onConfirmEntrySelected() {
- val selectedEntry = uiState.activeEntry?.activeEntryInfo
- if (selectedEntry != null) {
- onEntrySelected(selectedEntry)
- } else {
- Log.w("Account Selector",
- "Illegal state: confirm is pressed but activeEntry isn't set.")
- dialogResult.tryEmit(DialogResult(ResultState.COMPLETE))
+ fun onConfirmEntrySelected() {
+ val selectedEntry = uiState.activeEntry?.activeEntryInfo
+ if (selectedEntry != null) {
+ onEntrySelected(selectedEntry)
+ } else {
+ Log.d(Constants.LOG_TAG,
+ "Unexpected: confirm is pressed but no active entry exists.")
+ onInternalError()
+ }
}
- }
- fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
- val entry = uiState.selectedEntry
- val resultCode = providerActivityResult.resultCode
- val resultData = providerActivityResult.data
- if (resultCode == Activity.RESULT_CANCELED) {
- // Re-display the CredMan UI if the user canceled from the provider UI.
- uiState = uiState.copy(
- selectedEntry = null,
- hidden = false,
- providerActivityPending = false,
- )
- } else {
- if (entry != null) {
- val providerId = entry.providerId
- Log.d("Account Selector", "Got provider activity result: {provider=" +
- "$providerId, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " +
- "resultCode=$resultCode, resultData=$resultData}"
- )
- credManRepo.onOptionSelected(
- providerId, entry.entryKey, entry.entrySubkey, resultCode, resultData,
- )
- } else {
- Log.w("Account Selector",
- "Illegal state: received a provider result but found no matching entry.")
- }
- dialogResult.tryEmit(DialogResult(ResultState.COMPLETE))
+ fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
+ val entry = uiState.selectedEntry
+ val resultCode = providerActivityResult.resultCode
+ val resultData = providerActivityResult.data
+ if (resultCode == Activity.RESULT_CANCELED) {
+ // Re-display the CredMan UI if the user canceled from the provider UI.
+ Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
+ " re-displaying our UI.")
+ uiState = uiState.copy(
+ selectedEntry = null,
+ providerActivityState = ProviderActivityState.NOT_APPLICABLE,
+ )
+ } else {
+ if (entry != null) {
+ val providerId = entry.providerId
+ Log.d(Constants.LOG_TAG, "Got provider activity result: {provider=" +
+ "$providerId, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " +
+ "resultCode=$resultCode, resultData=$resultData}"
+ )
+ credManRepo.onOptionSelected(
+ providerId, entry.entryKey, entry.entrySubkey, resultCode, resultData,
+ )
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ } else {
+ Log.d(Constants.LOG_TAG,
+ "Illegal state: received a provider result but found no matching entry.")
+ onInternalError()
+ }
+ }
}
- }
+
+ companion object Factory {
+ // Validates the input and returns null if the input is invalid.
+ fun newInstance(
+ credManRepo: CredentialManagerRepo,
+ providerEnableListUiState: List<EnabledProviderInfo>,
+ providerDisableListUiState: List<DisabledProviderInfo>,
+ requestDisplayInfoUiState: RequestDisplayInfo?,
+ ): CreateCredentialViewModel? {
+ if (providerEnableListUiState.isEmpty() || requestDisplayInfoUiState == null) {
+ return null
+ }
+ return try {
+ val result = CreateCredentialViewModel(
+ credManRepo = credManRepo,
+ providerEnableListUiState = providerEnableListUiState,
+ providerDisableListUiState = providerDisableListUiState,
+ requestDisplayInfoUiState = requestDisplayInfoUiState
+ )
+ result
+ } catch (e: Exception) {
+ null
+ }
+ }
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 957488f..12a5085 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -19,6 +19,25 @@
import android.app.PendingIntent
import android.content.Intent
import android.graphics.drawable.Drawable
+import com.android.credentialmanager.common.DialogState
+import com.android.credentialmanager.common.ProviderActivityState
+
+data class CreateCredentialUiState(
+ val enabledProviders: List<EnabledProviderInfo>,
+ val disabledProviders: List<DisabledProviderInfo>? = null,
+ val currentScreenState: CreateScreenState,
+ val requestDisplayInfo: RequestDisplayInfo,
+ val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+ // Should not change with the real time update of default provider, only determine whether
+ // we're showing provider selection page at the beginning
+ val hasDefaultProvider: Boolean,
+ val activeEntry: ActiveEntry? = null,
+ val selectedEntry: EntryInfo? = null,
+ val providerActivityState: ProviderActivityState =
+ ProviderActivityState.NOT_APPLICABLE,
+ val isFromProviderSelection: Boolean? = null,
+ val dialogState: DialogState = DialogState.ACTIVE,
+)
open class ProviderInfo(
val icon: Drawable,
@@ -28,17 +47,17 @@
class EnabledProviderInfo(
icon: Drawable,
- name: String,
+ id: String,
displayName: String,
var createOptions: List<CreateOptionInfo>,
var remoteEntry: RemoteInfo?,
-) : ProviderInfo(icon, name, displayName)
+) : ProviderInfo(icon, id, displayName)
class DisabledProviderInfo(
icon: Drawable,
- name: String,
+ id: String,
displayName: String,
-) : ProviderInfo(icon, name, displayName)
+) : ProviderInfo(icon, id, displayName)
open class EntryInfo (
val providerId: String,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 420b4d5..b30d1ec 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -59,6 +59,7 @@
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetLayout
import com.android.credentialmanager.common.material.ModalBottomSheetValue
import com.android.credentialmanager.common.material.rememberModalBottomSheetState
@@ -90,30 +91,42 @@
modifier = Modifier.background(Color.Transparent),
sheetState = state,
sheetContent = {
- // TODO: hide UI at top level
- if (!uiState.hidden) {
- if (uiState.currentScreenState == GetScreenState.PRIMARY_SELECTION) {
- PrimarySelectionCard(
- requestDisplayInfo = uiState.requestDisplayInfo,
- providerDisplayInfo = uiState.providerDisplayInfo,
- providerInfoList = uiState.providerInfoList,
- activeEntry = uiState.activeEntry,
- onEntrySelected = viewModel::onEntrySelected,
- onConfirm = viewModel::onConfirmEntrySelected,
- onMoreOptionSelected = viewModel::onMoreOptionSelected,
- )
- } else {
- AllSignInOptionCard(
- providerInfoList = uiState.providerInfoList,
- providerDisplayInfo = uiState.providerDisplayInfo,
- onEntrySelected = viewModel::onEntrySelected,
- onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
- onCancel = viewModel::onCancel,
- isNoAccount = uiState.isNoAccount,
- )
+ // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim
+ // background color even when the content should be hidden while waiting for
+ // results from the provider app.
+ when (uiState.providerActivityState) {
+ ProviderActivityState.NOT_APPLICABLE -> {
+ if (uiState.currentScreenState == GetScreenState.PRIMARY_SELECTION) {
+ PrimarySelectionCard(
+ requestDisplayInfo = uiState.requestDisplayInfo,
+ providerDisplayInfo = uiState.providerDisplayInfo,
+ providerInfoList = uiState.providerInfoList,
+ activeEntry = uiState.activeEntry,
+ onEntrySelected = viewModel::onEntrySelected,
+ onConfirm = viewModel::onConfirmEntrySelected,
+ onMoreOptionSelected = viewModel::onMoreOptionSelected,
+ )
+ } else {
+ AllSignInOptionCard(
+ providerInfoList = uiState.providerInfoList,
+ providerDisplayInfo = uiState.providerDisplayInfo,
+ onEntrySelected = viewModel::onEntrySelected,
+ onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
+ onCancel = viewModel::onCancel,
+ isNoAccount = uiState.isNoAccount,
+ )
+ }
}
- } else if (uiState.selectedEntry != null && !uiState.providerActivityPending) {
- viewModel.launchProviderUi(providerActivityLauncher)
+ ProviderActivityState.READY_TO_LAUNCH -> {
+ // Launch only once per providerActivityState change so that the provider
+ // UI will not be accidentally launched twice.
+ LaunchedEffect(uiState.providerActivityState) {
+ viewModel.launchProviderUi(providerActivityLauncher)
+ }
+ }
+ ProviderActivityState.PENDING -> {
+ // Hide our content when the provider activity is active.
+ }
}
},
scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f),
@@ -225,7 +238,7 @@
color = Color.Transparent
)
var totalEntriesCount = sortedUserNameToCredentialEntryList
- .flatMap{ it.sortedCredentialEntryList}.size + authenticationEntryList
+ .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList
.size + providerInfoList.flatMap { it.actionEntryList }.size
if (providerDisplayInfo.remoteEntry != null) totalEntriesCount += 1
// Row horizontalArrangement differs on only one actionButton(should place on most
@@ -528,10 +541,10 @@
credentialEntryInfo.credentialTypeDisplayName
else
credentialEntryInfo.credentialTypeDisplayName +
- stringResource(
- R.string.get_dialog_sign_in_type_username_separator
- ) +
- credentialEntryInfo.displayName
+ stringResource(
+ R.string.get_dialog_sign_in_type_username_separator
+ ) +
+ credentialEntryInfo.displayName
},
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp, start = 5.dp)
@@ -561,7 +574,7 @@
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp),
- ) {
+ ) {
Column() {
// TODO: fix the text values.
TextOnSurfaceVariant(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
index c72ae72..7d2f0da 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
@@ -26,251 +26,257 @@
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.android.credentialmanager.CredentialManagerRepo
-import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.Constants
+import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
-import com.android.credentialmanager.common.ResultState
+import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
import com.android.internal.util.Preconditions
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
data class GetCredentialUiState(
- val providerInfoList: List<ProviderInfo>,
- val requestDisplayInfo: RequestDisplayInfo,
- val currentScreenState: GetScreenState = toGetScreenState(providerInfoList),
- val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
- val selectedEntry: EntryInfo? = null,
- val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
- val hidden: Boolean = false,
- val providerActivityPending: Boolean = false,
- val isNoAccount: Boolean = false,
+ val providerInfoList: List<ProviderInfo>,
+ val requestDisplayInfo: RequestDisplayInfo,
+ val currentScreenState: GetScreenState = toGetScreenState(providerInfoList),
+ val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
+ val selectedEntry: EntryInfo? = null,
+ val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
+ val providerActivityState: ProviderActivityState =
+ ProviderActivityState.NOT_APPLICABLE,
+ val isNoAccount: Boolean = false,
+ val dialogState: DialogState = DialogState.ACTIVE,
)
-class GetCredentialViewModel(private val credManRepo: CredentialManagerRepo) : ViewModel() {
+class GetCredentialViewModel(
+ private val credManRepo: CredentialManagerRepo,
+ initialUiState: GetCredentialUiState,
+) : ViewModel() {
- var uiState by mutableStateOf(credManRepo.getCredentialInitialUiState())
- private set
+ var uiState by mutableStateOf(initialUiState)
+ private set
- val dialogResult: MutableSharedFlow<DialogResult> =
- MutableSharedFlow(replay = 0, extraBufferCapacity = 1,
- onBufferOverflow = BufferOverflow.DROP_OLDEST)
-
- fun observeDialogResult(): SharedFlow<DialogResult> {
- return dialogResult
- }
-
- fun onEntrySelected(entry: EntryInfo) {
- Log.d("Account Selector", "credential selected:" +
- " {provider=${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
- if (entry.pendingIntent != null) {
- uiState = uiState.copy(
- selectedEntry = entry,
- hidden = true,
- )
- } else {
- credManRepo.onOptionSelected(entry.providerId, entry.entryKey, entry.entrySubkey)
- dialogResult.tryEmit(DialogResult(ResultState.COMPLETE))
+ fun onEntrySelected(entry: EntryInfo) {
+ Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" +
+ ", key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
+ if (entry.pendingIntent != null) {
+ uiState = uiState.copy(
+ selectedEntry = entry,
+ providerActivityState = ProviderActivityState.READY_TO_LAUNCH,
+ )
+ } else {
+ credManRepo.onOptionSelected(entry.providerId, entry.entryKey, entry.entrySubkey)
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ }
}
- }
- fun onConfirmEntrySelected() {
- val activeEntry = uiState.activeEntry
- if (activeEntry != null) {
- onEntrySelected(activeEntry)
- } else {
- Log.w("Account Selector",
- "Illegal state: confirm is pressed but activeEntry isn't set.")
- dialogResult.tryEmit(DialogResult(ResultState.COMPLETE))
+ fun onConfirmEntrySelected() {
+ val activeEntry = uiState.activeEntry
+ if (activeEntry != null) {
+ onEntrySelected(activeEntry)
+ } else {
+ Log.d(Constants.LOG_TAG,
+ "Illegal state: confirm is pressed but activeEntry isn't set.")
+ onInternalError()
+ }
}
- }
- fun launchProviderUi(
- launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
- ) {
- val entry = uiState.selectedEntry
- if (entry != null && entry.pendingIntent != null) {
- uiState = uiState.copy(
- providerActivityPending = true,
- )
- val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
- .setFillInIntent(entry.fillInIntent).build()
- launcher.launch(intentSenderRequest)
- } else {
- Log.w("Account Selector", "No provider UI to launch")
+ fun launchProviderUi(
+ launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
+ ) {
+ val entry = uiState.selectedEntry
+ if (entry != null && entry.pendingIntent != null) {
+ Log.d(Constants.LOG_TAG, "Launching provider activity")
+ uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
+ val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
+ .setFillInIntent(entry.fillInIntent).build()
+ launcher.launch(intentSenderRequest)
+ } else {
+ Log.d(Constants.LOG_TAG, "No provider UI to launch")
+ onInternalError()
+ }
}
- }
- fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
- val entry = uiState.selectedEntry
- val resultCode = providerActivityResult.resultCode
- val resultData = providerActivityResult.data
- if (resultCode == Activity.RESULT_CANCELED) {
- // Re-display the CredMan UI if the user canceled from the provider UI.
- uiState = uiState.copy(
- selectedEntry = null,
- hidden = false,
- providerActivityPending = false,
- )
- } else {
- if (entry != null) {
- Log.d("Account Selector", "Got provider activity result: {provider=" +
- "${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " +
- "resultCode=$resultCode, resultData=$resultData}"
+ // When the view model runs into unexpected illegal state, reports the error back and close
+ // the activity gracefully.
+ private fun onInternalError() {
+ Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state")
+ credManRepo.onParsingFailureCancel()
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ }
+
+ fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
+ val entry = uiState.selectedEntry
+ val resultCode = providerActivityResult.resultCode
+ val resultData = providerActivityResult.data
+ if (resultCode == Activity.RESULT_CANCELED) {
+ // Re-display the CredMan UI if the user canceled from the provider UI.
+ Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
+ " re-displaying our UI.")
+ uiState = uiState.copy(
+ selectedEntry = null,
+ providerActivityState = ProviderActivityState.NOT_APPLICABLE,
+ )
+ } else {
+ if (entry != null) {
+ Log.d(
+ Constants.LOG_TAG, "Got provider activity result: {provider=" +
+ "${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}" +
+ ", resultCode=$resultCode, resultData=$resultData}"
+ )
+ credManRepo.onOptionSelected(
+ entry.providerId, entry.entryKey, entry.entrySubkey,
+ resultCode, resultData,
+ )
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ } else {
+ Log.w(Constants.LOG_TAG,
+ "Illegal state: received a provider result but found no matching entry.")
+ onInternalError()
+ }
+ }
+ }
+
+ fun onMoreOptionSelected() {
+ Log.d(Constants.LOG_TAG, "More Option selected")
+ uiState = uiState.copy(
+ currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS
)
- credManRepo.onOptionSelected(
- entry.providerId, entry.entryKey, entry.entrySubkey,
- resultCode, resultData,
- )
- } else {
- Log.w("Account Selector",
- "Illegal state: received a provider result but found no matching entry.")
- }
- dialogResult.tryEmit(DialogResult(ResultState.COMPLETE))
}
- }
- fun onMoreOptionSelected() {
- Log.d("Account Selector", "More Option selected")
- uiState = uiState.copy(
- currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS
- )
- }
+ fun onMoreOptionOnSnackBarSelected(isNoAccount: Boolean) {
+ Log.d(Constants.LOG_TAG, "More Option on snackBar selected")
+ uiState = uiState.copy(
+ currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS,
+ isNoAccount = isNoAccount,
+ )
+ }
- fun onMoreOptionOnSnackBarSelected(isNoAccount: Boolean) {
- Log.d("Account Selector", "More Option on snackBar selected")
- uiState = uiState.copy(
- currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS,
- isNoAccount = isNoAccount,
- )
- }
+ fun onBackToPrimarySelectionScreen() {
+ uiState = uiState.copy(
+ currentScreenState = GetScreenState.PRIMARY_SELECTION
+ )
+ }
- fun onBackToPrimarySelectionScreen() {
- uiState = uiState.copy(
- currentScreenState = GetScreenState.PRIMARY_SELECTION
- )
- }
-
- fun onCancel() {
- credManRepo.onUserCancel()
- dialogResult.tryEmit(DialogResult(ResultState.NORMAL_CANCELED))
- }
+ fun onCancel() {
+ credManRepo.onUserCancel()
+ uiState = uiState.copy(dialogState = DialogState.COMPLETE)
+ }
}
private fun toProviderDisplayInfo(
- providerInfoList: List<ProviderInfo>
+ providerInfoList: List<ProviderInfo>
): ProviderDisplayInfo {
- val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()
- val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>()
- val remoteEntryList = mutableListOf<RemoteEntryInfo>()
- providerInfoList.forEach { providerInfo ->
- if (providerInfo.authenticationEntry != null) {
- authenticationEntryList.add(providerInfo.authenticationEntry)
- }
- if (providerInfo.remoteEntry != null) {
- remoteEntryList.add(providerInfo.remoteEntry)
- }
-
- providerInfo.credentialEntryList.forEach {
- userNameToCredentialEntryMap.compute(
- it.userName
- ) {
- _, v ->
- if (v == null) {
- mutableListOf(it)
- } else {
- v.add(it)
- v
+ val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()
+ val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>()
+ val remoteEntryList = mutableListOf<RemoteEntryInfo>()
+ providerInfoList.forEach { providerInfo ->
+ if (providerInfo.authenticationEntry != null) {
+ authenticationEntryList.add(providerInfo.authenticationEntry)
}
- }
+ if (providerInfo.remoteEntry != null) {
+ remoteEntryList.add(providerInfo.remoteEntry)
+ }
+
+ providerInfo.credentialEntryList.forEach {
+ userNameToCredentialEntryMap.compute(
+ it.userName
+ ) { _, v ->
+ if (v == null) {
+ mutableListOf(it)
+ } else {
+ v.add(it)
+ v
+ }
+ }
+ }
}
- }
- // There can only be at most one remote entry
- // TODO: fail elegantly
- Preconditions.checkState(remoteEntryList.size <= 1)
+ // There can only be at most one remote entry
+ // TODO: fail elegantly
+ Preconditions.checkState(remoteEntryList.size <= 1)
- // Compose sortedUserNameToCredentialEntryList
- val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp()
- // Sort per username
- userNameToCredentialEntryMap.values.forEach {
- it.sortWith(comparator)
- }
- // Transform to list of PerUserNameCredentialEntryLists and then sort across usernames
- val sortedUserNameToCredentialEntryList = userNameToCredentialEntryMap.map {
- PerUserNameCredentialEntryList(it.key, it.value)
- }.sortedWith(
- compareByDescending{ it.sortedCredentialEntryList.first().lastUsedTimeMillis }
- )
+ // Compose sortedUserNameToCredentialEntryList
+ val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp()
+ // Sort per username
+ userNameToCredentialEntryMap.values.forEach {
+ it.sortWith(comparator)
+ }
+ // Transform to list of PerUserNameCredentialEntryLists and then sort across usernames
+ val sortedUserNameToCredentialEntryList = userNameToCredentialEntryMap.map {
+ PerUserNameCredentialEntryList(it.key, it.value)
+ }.sortedWith(
+ compareByDescending { it.sortedCredentialEntryList.first().lastUsedTimeMillis }
+ )
- return ProviderDisplayInfo(
- sortedUserNameToCredentialEntryList = sortedUserNameToCredentialEntryList,
- authenticationEntryList = authenticationEntryList,
- remoteEntry = remoteEntryList.getOrNull(0),
- )
+ return ProviderDisplayInfo(
+ sortedUserNameToCredentialEntryList = sortedUserNameToCredentialEntryList,
+ authenticationEntryList = authenticationEntryList,
+ remoteEntry = remoteEntryList.getOrNull(0),
+ )
}
private fun toActiveEntry(
- providerDisplayInfo: ProviderDisplayInfo,
+ providerDisplayInfo: ProviderDisplayInfo,
): EntryInfo? {
- val sortedUserNameToCredentialEntryList =
- providerDisplayInfo.sortedUserNameToCredentialEntryList
- val authenticationEntryList = providerDisplayInfo.authenticationEntryList
- var activeEntry: EntryInfo? = null
- if (sortedUserNameToCredentialEntryList
- .size == 1 && authenticationEntryList.isEmpty()
- ) {
- activeEntry = sortedUserNameToCredentialEntryList.first().sortedCredentialEntryList.first()
- } else if (
- sortedUserNameToCredentialEntryList
- .isEmpty() && authenticationEntryList.size == 1
- ) {
- activeEntry = authenticationEntryList.first()
- }
- return activeEntry
+ val sortedUserNameToCredentialEntryList =
+ providerDisplayInfo.sortedUserNameToCredentialEntryList
+ val authenticationEntryList = providerDisplayInfo.authenticationEntryList
+ var activeEntry: EntryInfo? = null
+ if (sortedUserNameToCredentialEntryList
+ .size == 1 && authenticationEntryList.isEmpty()
+ ) {
+ activeEntry = sortedUserNameToCredentialEntryList.first().sortedCredentialEntryList.first()
+ } else if (
+ sortedUserNameToCredentialEntryList
+ .isEmpty() && authenticationEntryList.size == 1
+ ) {
+ activeEntry = authenticationEntryList.first()
+ }
+ return activeEntry
}
private fun toGetScreenState(
- providerInfoList: List<ProviderInfo>
+ providerInfoList: List<ProviderInfo>
): GetScreenState {
- var noLocalAccount = true
- var remoteInfo: RemoteEntryInfo? = null
- providerInfoList.forEach{providerInfo -> if (
- providerInfo.credentialEntryList.isNotEmpty() || providerInfo.authenticationEntry != null
- ) { noLocalAccount = false }
- // TODO: handle the error situation that if multiple remoteInfos exists
- if (providerInfo.remoteEntry != null) {
- remoteInfo = providerInfo.remoteEntry
+ var noLocalAccount = true
+ var remoteInfo: RemoteEntryInfo? = null
+ providerInfoList.forEach { providerInfo ->
+ if (providerInfo.credentialEntryList.isNotEmpty() ||
+ providerInfo.authenticationEntry != null) {
+ noLocalAccount = false
+ }
+ // TODO: handle the error situation that if multiple remoteInfos exists
+ if (providerInfo.remoteEntry != null) {
+ remoteInfo = providerInfo.remoteEntry
+ }
}
- }
- return if (noLocalAccount && remoteInfo != null)
- GetScreenState.REMOTE_ONLY else GetScreenState.PRIMARY_SELECTION
+ return if (noLocalAccount && remoteInfo != null)
+ GetScreenState.REMOTE_ONLY else GetScreenState.PRIMARY_SELECTION
}
internal class CredentialEntryInfoComparatorByTypeThenTimestamp : Comparator<CredentialEntryInfo> {
- override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int {
- // First prefer passkey type for its security benefits
- if (p0.credentialType != p1.credentialType) {
- if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p0.credentialType) {
- return -1
- } else if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p1.credentialType) {
- return 1
- }
- }
+ override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int {
+ // First prefer passkey type for its security benefits
+ if (p0.credentialType != p1.credentialType) {
+ if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p0.credentialType) {
+ return -1
+ } else if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p1.credentialType) {
+ return 1
+ }
+ }
- // Then order by last used timestamp
- if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) {
- if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) {
- return 1
- } else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
- return -1
- }
- } else if (p0.lastUsedTimeMillis != null && p0.lastUsedTimeMillis > 0) {
- return -1
- } else if (p1.lastUsedTimeMillis != null && p1.lastUsedTimeMillis > 0) {
- return 1
+ // Then order by last used timestamp
+ if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) {
+ if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) {
+ return 1
+ } else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
+ return -1
+ }
+ } else if (p0.lastUsedTimeMillis != null && p0.lastUsedTimeMillis > 0) {
+ return -1
+ } else if (p1.lastUsedTimeMillis != null && p1.lastUsedTimeMillis > 0) {
+ return 1
+ }
+ return 0
}
- return 0
- }
}
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 37f2930..bc3a9c8 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Program geïnstalleer."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Wil jy hierdie program installeer?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Wil jy hierdie program opdateer?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Opdaterings vir hierdie app word tans deur <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> bestuur.\n\nAs jy opdateer, sal jy toekomstige opdaterings eerder van <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> af ontvang."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Opdaterings vir hierdie app word tans deur <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> bestuur.\n\nWil jy hierdie opdatering vanaf <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> installeer?"</string>
<string name="install_failed" msgid="5777824004474125469">"Program nie geïnstalleer nie."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Die installering van die pakket is geblokkeer."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Program is nie geïnstalleer nie omdat pakket met \'n bestaande pakket bots."</string>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
index 83966db..de53a5e 100644
--- a/packages/PackageInstaller/res/values-am/strings.xml
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"መተግበሪያ ተጭኗል።"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ይህን መተግበሪያ መጫን ይፈልጋሉ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ይህን መተግበሪያ ማዘመን ይፈልጋሉ?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"የዚህ መተግበሪያ ዝማኔዎች አሁን በ<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> በመተዳደር ላይ ናቸው።\n\nበማዘመንዎ የወደፊት ዝማኔዎችን በምትኩ ከ<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ያገኛሉ።"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"የዚህ መተግበሪያ ዝማኔዎች አሁን በ<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> በመተዳደር ላይ ናቸው።\n\nይህን ዝማኔ ከ<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> መጫን ይፈልጋሉ።"</string>
<string name="install_failed" msgid="5777824004474125469">"መተግበሪያ አልተጫነም።"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ጥቅሉ እንዳይጫን ታግዷል።"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"እንደ ጥቅል ያልተጫነ መተግበሪያ ከነባር ጥቅል ጋር ይጋጫል።"</string>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index c7dbf08..f41115b 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"تم تثبيت التطبيق."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"هل تريد تثبيت هذا التطبيق؟"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"هل تريد تحديث هذا التطبيق؟"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"تتم إدارة تحديثات هذا التطبيق حاليًا من قِبل \"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>\".\n\nمن خلال التحديث، ستتلقّى التحديثات المتوفّرة من \"<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>\" بدلاً من ذلك."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"تتم إدارة تحديثات هذا التطبيق حاليًا من قِبل \"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>\".\n\nهل تريد تثبيت هذا التحديث من <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>\"؟"</string>
<string name="install_failed" msgid="5777824004474125469">"التطبيق ليس مثبتًا."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"تم حظر تثبيت الحزمة."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"لم يتم تثبيت التطبيق لأن حزمة التثبيت تتعارض مع حزمة حالية."</string>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
index c456268..08758a1 100644
--- a/packages/PackageInstaller/res/values-as/strings.xml
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"এপ্ ইনষ্টল কৰা হ’ল।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"আপুনি এই এপ্টো ইনষ্টল কৰিবলৈ বিচাৰেনে?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"আপুনি এই এপ্টো আপডে’ট কৰিবলৈ বিচাৰেনে?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"এই এপ্টোৰ আপডে’টসমূহ বৰ্তমান <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>এ পৰিচালনা কৰি আছে।\n\nআপডে’ট কৰিলে, আপুনি ইয়াৰ পৰিবৰ্তে <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>ৰ পৰা ভৱিষ্যত আপডে’টসমূহ পাব।"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"এই এপ্টোৰ আপডে’টসমূহ বৰ্তমান <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>এ পৰিচালনা কৰি আছে।\n\nআপুনি <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>ৰ পৰা অহা এই আপডে’টটো ইনষ্টল কৰিব বিচাৰেনে?"</string>
<string name="install_failed" msgid="5777824004474125469">"এপ্ ইনষ্টল কৰা হোৱা নাই।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"পেকেজটোৰ ইনষ্টল অৱৰোধ কৰা হৈছে।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"এপ্টো ইনষ্টল কৰিব পৰা নগ\'ল কাৰণ ইয়াৰ সৈতে আগৰে পৰা থকা এটা পেকেজৰ সংঘাত হৈছে।"</string>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
index 749254a..d6e117f 100644
--- a/packages/PackageInstaller/res/values-az/strings.xml
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Tətbiq quraşdırılıb."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Bu tətbiqi quraşdırmaq istəyirsiniz?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Bu tətbiqi güncəlləmək istəyirsiniz?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Bu tətbiq üzrə güncəlləmələr hazırda <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> tərəfindən idarə olunur.\n\nGüncəlləməklə, <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> adlı mənbədən gələcək güncəlləmələri əldə edəcəksiniz."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Bu tətbiq üzrə güncəlləmələr hazırda <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> tərəfindən idarə olunur.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> adlı mənbədən bu güncəlləməni quraşdırmaq istəyirsinizmi."</string>
<string name="install_failed" msgid="5777824004474125469">"Tətbiq quraşdırılmayıb."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketin quraşdırılması blok edildi."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Bu paketin mövcud paket ilə ziddiyətinə görə tətbiq quraşdırılmadı."</string>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
index b2943a0..13bd74b 100644
--- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je instalirana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Želite da instalirate ovu aplikaciju?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Želite da ažurirate ovu aplikaciju?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Ažuriranjima ove aplikacije trenutno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nAko ažurirate, buduća ažuriranja ćete dobijati od vlasnika <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Ažuriranjima ove aplikacije trenutno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nŽelite li da instalirate ovo ažuriranje vlasnika <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija nije instalirana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instaliranje paketa je blokirano."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija nije instalirana jer je paket neusaglašen sa postojećim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index fa64fa6..9e7a0af 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Праграма ўсталявана."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Усталяваць гэту праграму?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Абнавіць гэту праграму?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Абнаўленнямі гэтай праграмы цяпер кіруе <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nВыканаўшы гэта абнаўленне, усе наступныя вы будзеце атрымліваць ад <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Абнаўленнямі гэтай праграмы цяпер кіруе <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nВы хочаце ўсталяваць гэта абнаўленне ад <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Праграма не ўсталявана."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Усталяванне пакета заблакіравана."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Праграма не ўсталявана, таму што пакет канфліктуе з існуючым пакетам."</string>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index 0fb7aa5..140cd2f 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Приложението бе инсталирано."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Искате ли да инсталирате това приложение?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Искате ли да актуализирате това приложение?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Актуализациите на това приложение понастоящем се управляват от <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nСлед актуализирането ще получавате бъдещите актуализации от <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Актуализациите на това приложение понастоящем се управляват от <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nИскате ли да инсталирате тази актуализация от <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Приложението не бе инсталирано."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Инсталирането на пакета бе блокирано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Приложението не бе инсталирано, тъй като пакетът е в конфликт със съществуващ пакет."</string>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
index 52e5a32..ec64f9b 100644
--- a/packages/PackageInstaller/res/values-bn/strings.xml
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"অ্যাপটি ইনস্টল করা হয়ে গেছে।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"আপনি কি এই অ্যাপটি ইনস্টল করতে চান?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"আপনি কি এই অ্যাপটি আপডেট করতে চান?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"এই অ্যাপের আপডেট বর্তমানে <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ম্যানেজ করছেন।\n\nআপডেট করা হলে, আপনি পরিবর্তে <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>-এর থেকে ভবিষ্যতের আপডেট পাবেন।"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"বর্তমানে <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> এই অ্যাপের আপডেট ম্যানেজ করছেন।\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> থেকে আসা এই আপডেট ইনস্টল করতে চান।"</string>
<string name="install_failed" msgid="5777824004474125469">"অ্যাপটি ইনস্টল করা হয়নি।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ইনস্টল হওয়া থেকে প্যাকেজটিকে ব্লক করা হয়েছে।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"আগে থেকেই থাকা একটি প্যাকেজের সাথে প্যাকেজটির সমস্যা সৃষ্টি হওয়ায় অ্যাপটি ইনস্টল করা যায়নি।"</string>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
index 01d160b..2f849a2 100644
--- a/packages/PackageInstaller/res/values-bs/strings.xml
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je instalirana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Želite li instalirati ovu aplikaciju?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Želite li ažurirati ovu aplikaciju?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Ažuriranjima ove aplikacije trenutno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nAžuriranjem ćete dobivati buduća ažuriranja od <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Ažuriranjima ove aplikacije trenutno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nŽelite li instalirati ažuriranje od <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija nije instalirana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instaliranje ovog paketa je blokirano."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija nije instalirana jer paket nije usaglašen s postojećim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index 7b42cef..9b75fe3 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"S\'ha instal·lat l\'aplicació."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vols instal·lar aquesta aplicació?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vols actualitzar aquesta aplicació?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Actualment, <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> gestiona les actualitzacions d\'aquesta aplicació.\n\nSi l\'actualitzes, obtindràs novetats de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> en el futur."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Actualment, <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> gestiona les actualitzacions d\'aquesta aplicació.\n\nVols instal·lar aquesta actualització de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"No s\'ha instal·lat l\'aplicació."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"El paquet s\'ha bloquejat perquè no es pugui instal·lar."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"L\'aplicació no s\'ha instal·lat perquè el paquet entra en conflicte amb un d\'existent."</string>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
index 7e5c881..c556952 100644
--- a/packages/PackageInstaller/res/values-cs/strings.xml
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikace je nainstalována."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Chcete tuto aplikaci nainstalovat?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Chcete tuto aplikaci aktualizovat?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Aktualizace této aplikace aktuálně spravuje vlastník <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nPokud ji aktualizujete, budete místo toho v budoucnu dostávat aktualizace od vlastníka <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Aktualizace této aplikace aktuálně spravuje vlastník <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nChcete nainstalovat tuto aktualizaci od vlastníka <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikaci nelze nainstalovat."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalace balíčku byla zablokována."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikaci nelze nainstalovat, protože balíček je v konfliktu se stávajícím balíčkem."</string>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index 2441f85..7f47029 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Appen er installeret."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vil du installere denne app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vil du opdatere denne app?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Opdateringer af denne app administreres i øjeblikket af <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nNår du opdaterer, vil du fremover få opdateringer fra <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> i stedet."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Opdateringer af denne app administreres i øjeblikket af <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVil du installere denne opdatering fra <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Appen blev ikke installeret."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakken blev forhindret i at blive installeret."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen blev ikke installeret, da pakken er i strid med en eksisterende pakke."</string>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index 24cfc55..dea95e8 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"App wurde installiert."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Möchtest du diese App installieren?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Möchtest du diese App aktualisieren?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Updates für diese App werden momentan von <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> verwaltet.\n\nWenn du sie aktualisierst, erhältst du zukünftige Updates stattdessen von <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Updates für diese App werden momentan von <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> verwaltet.\n\nMöchtest du dieses Update von <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> installieren?"</string>
<string name="install_failed" msgid="5777824004474125469">"App wurde nicht installiert."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Die Installation des Pakets wurde blockiert."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Die App wurde nicht installiert, da das Paket in Konflikt mit einem bestehenden Paket steht."</string>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index ce8356b..3c3eae8 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Η εφαρμογή εγκαταστάθηκε."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Θέλετε να εγκαταστήσετε αυτήν την εφαρμογή;"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Θέλετε να ενημερώσετε αυτήν την εφαρμογή;"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Η διαχείριση των ενημερώσεων σε αυτήν την εφαρμογή πραγματοποιείται προς το παρόν από τον κάτοχο <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nΚάνοντας την ενημέρωση, θα λαμβάνετε αντ\' αυτού τις μελλοντικές ενημερώσεις από τον κάτοχο <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Η διαχείριση των ενημερώσεων σε αυτήν την εφαρμογή πραγματοποιείται προς το παρόν από τον κάτοχο <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nΘέλετε να εγκαταστήσετε αυτήν την ενημέρωση από τον κάτοχο <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Η εφαρμογή δεν εγκαταστάθηκε."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Η εγκατάσταση του πακέτου αποκλείστηκε."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Η εφαρμογή δεν εγκαταστάθηκε, επειδή το πακέτο είναι σε διένεξη με κάποιο υπάρχον πακέτο."</string>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
index b553986..9d44fcc 100644
--- a/packages/PackageInstaller/res/values-es-rUS/strings.xml
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Se instaló la app."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"¿Deseas instalar esta app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"¿Deseas actualizar esta app?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"En este momento, las actualizaciones de esta app están administradas por <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nSi continúas con la actualización, recibirás actualizaciones futuras de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"En este momento, las actualizaciones de esta app están administradas por <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\n¿Quieres instalar esta actualización de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"No se instaló la app."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Se bloqueó el paquete para impedir la instalación."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"No se instaló la app debido a un conflicto con un paquete."</string>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 66e6bc3..bedb822 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplicación instalada."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"¿Quieres instalar esta aplicación?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"¿Quieres actualizar esta aplicación?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Las actualizaciones de esta aplicación las gestiona <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nSi actualizas, recibirás las futuras actualizaciones por parte de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Las actualizaciones de esta aplicación las gestiona <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\n¿Quieres instalar esta actualización de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"No se ha instalado la aplicación."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Se ha bloqueado la instalación del paquete."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"La aplicación no se ha instalado debido a un conflicto con un paquete."</string>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
index 742f663..38a6fbf 100644
--- a/packages/PackageInstaller/res/values-et/strings.xml
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Rakendus on installitud."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Kas soovite selle rakenduse installida?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Kas soovite seda rakendust värskendada?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Selle rakenduse värskendusi haldab praegu <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVärskendamisel saate tulevasi värskendusi hoopis omanikult <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Selle rakenduse värskendusi haldab praegu <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nKas soovite installida omaniku <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> värskenduse."</string>
<string name="install_failed" msgid="5777824004474125469">"Rakendus pole installitud."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketi installimine blokeeriti."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Rakendust ei installitud, kuna pakett on olemasoleva paketiga vastuolus."</string>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index 4687ab5..ad2a5be 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Instalatu da aplikazioa."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Aplikazioa instalatu nahi duzu?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Aplikazioa eguneratu nahi duzu?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> arduratzen da aplikazio hau eguneratzeaz.\n\n Eguneratuz gero, hemendik aurrera <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> arduratuko da eguneratzeez."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> arduratzen da aplikazio hau eguneratzeaz.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> bidez jasotako eguneratze hau instalatu nahi duzu?"</string>
<string name="install_failed" msgid="5777824004474125469">"Ez da instalatu aplikazioa."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketea instalatzeko aukera blokeatu egin da."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Ez da instalatu aplikazioa, gatazka bat sortu delako lehendik dagoen pakete batekin."</string>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
index dfe0873..2725afa 100644
--- a/packages/PackageInstaller/res/values-fa/strings.xml
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"برنامه نصب شد."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"میخواهید این برنامه را نصب کنید؟"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"میخواهید این برنامه را بهروزرسانی کنید؟"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"درحالحاضر <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> بهروزرسانیهای این برنامه را مدیریت میکند.\n\nبا بهروز کردن، بهروزرسانیهای آتی را بهجای مالک قبلی از <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> دریافت خواهید کرد."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"درحالحاضر <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> بهروزرسانیهای این برنامه را مدیریت میکند.\n\nمیخواهید این بهروزرسانی را از <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> نصب کنید؟"</string>
<string name="install_failed" msgid="5777824004474125469">"برنامه نصب نشد."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"از نصب شدن بسته جلوگیری شد."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"برنامه نصب نشد چون بسته با بسته موجود تداخل دارد."</string>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
index 9a76c91..0a7486d 100644
--- a/packages/PackageInstaller/res/values-fi/strings.xml
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Sovellus on asennettu."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Haluatko asentaa tämän sovelluksen?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Haluatko päivittää tämän sovelluksen?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> hallitsee tämän sovelluksen päivityksiä.\n\nPäivittämisen jälkeen päivityksiä hallitsee <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> hallitsee tämän sovelluksen päivityksiä.\n\nHaluatko ladata tämän päivityksen täältä: <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Sovellusta ei asennettu."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketin asennus estettiin."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Sovellusta ei asennettu, koska paketti on ristiriidassa nykyisen paketin kanssa."</string>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index 9be6f5a..1377f25 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Application installée."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Voulez-vous installer cette application?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Voulez-vous mettre à jour cette application?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Les mises à jour pour cette application sont actuellement gérées par <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nEn effectuant une mise à jour, vous recevrez plutôt les futures mises à jour de la part de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Les mises à jour de cette application sont actuellement gérées par <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVoulez-vous installer cette mise à jour de la part de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Application non installée."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"L\'installation du paquet a été bloquée."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"L\'application n\'a pas été installée, car le paquet entre en conflit avec un paquet existant."</string>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 7744fabe..5eaea19 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Application installée."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Voulez-vous installer cette appli ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Voulez-vous mettre à jour cette appli ?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Les mises à jour de cette appli sont actuellement gérées par <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nEn procédant à la mise à jour, vous recevrez les futures mises à jour de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> à la place."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Les mises à jour de cette appli sont actuellement gérées par <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVoulez-vous installer cette mise à jour de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ?"</string>
<string name="install_failed" msgid="5777824004474125469">"Application non installée."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"L\'installation du package a été bloquée."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"L\'application n\'a pas été installée, car le package est en conflit avec un package déjà présent."</string>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index 6ba8813..205cb26 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Instalouse a aplicación."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Queres instalar esta aplicación?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Queres actualizar esta aplicación?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Actualmente, as actualizacións desta aplicación xestiónaas <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nSe a actualizas, as futuras actualizacións recibiralas de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Actualmente, as actualizacións desta aplicación xestiónaas <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nQueres instalar esta actualización de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Non se instalou a aplicación"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Bloqueouse a instalación do paquete."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"A aplicación non se instalou porque o paquete presenta un conflito cun paquete que xa hai."</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index d139b2b..ab752e6 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ઍપ્લિકેશન ઇન્સ્ટૉલ કરી."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"શું તમે આ ઍપ ઇન્સ્ટૉલ કરવા માગો છો?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"શું તમે આ ઍપ અપડેટ કરવા માગો છો?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"આ ઍપની અપડેટને હાલમાં <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> દ્વારા મેનેજ કરવામાં આવે છે.\n\nઅપડેટ કરવાથી, તમને તેના બદલે <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> તરફથી ભવિષ્યની અપડેટ પ્રાપ્ત થશે."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"આ ઍપની અપડેટને હાલમાં <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> દ્વારા મેનેજ કરવામાં આવે છે.\n\nશું તમે <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> તરફથી આ અપડેટ ઇન્સ્ટૉલ કરવા માગો છો."</string>
<string name="install_failed" msgid="5777824004474125469">"ઍપ્લિકેશન ઇન્સ્ટૉલ કરી નથી."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"પૅકેજને ઇન્સ્ટૉલ થવાથી બ્લૉક કરવામાં આવ્યું હતું."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"પૅકેજનો અસ્તિત્વમાંના પૅકેજ સાથે વિરોધાભાસ હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."</string>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index 17cf962..3bb636d 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ऐप्लिकेशन इंस्टॉल हो गया."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"क्या आपको यह ऐप्लिकेशन इंस्टॉल करना है?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"क्या आप इस ऐप्लिकेशन को अपडेट करना चाहते हैं?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"फ़िलहाल, इस ऐप्लिकेशन के अपडेट <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> मैनेज करता है.\n\nऐप्लिकेशन अपडेट करने के बाद, नए अपडेट <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> से मिलेंगे."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"फ़िलहाल, इस ऐप्लिकेशन के अपडेट <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> मैनेज करता है.\n\nक्या यह अपडेट <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> से करवाना है."</string>
<string name="install_failed" msgid="5777824004474125469">"ऐप्लिकेशन इंस्टॉल नहीं हुआ."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"पैकेज को इंस्टॉल होने से ब्लॉक किया हुआ है."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि पैकेज का किसी मौजूदा पैकेज से विरोध है."</string>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
index 9b7eeea..2eb3434 100644
--- a/packages/PackageInstaller/res/values-hr/strings.xml
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je instalirana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Želite li instalirati ovu aplikaciju?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Želite li ažurirati ovu aplikaciju?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Ažuriranjima ove aplikacije trenutačno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nAko je ažurirate, buduća ažuriranja primat ćete od vlasnika <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Ažuriranjima ove aplikacije trenutačno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nŽelite li instalirati ažuriranje od vlasnika <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija nije instalirana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instaliranje paketa blokirano je."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija koja nije instalirana kao paket u sukobu je s postojećim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
index 10e79a6..fc2d34b 100644
--- a/packages/PackageInstaller/res/values-hu/strings.xml
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Alkalmazás telepítve."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Telepíti ezt az alkalmazást?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Frissíti ezt az alkalmazást?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Az alkalmazást érintő frissítéseket jelenleg <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> kezeli.\n\nA mostani frissítés után a jövőbeli frissítéseket helyette tőle érkeznek majd: <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Az alkalmazást érintő frissítéseket jelenleg <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> kezeli.\n\nSzeretné telepíteni a tőle származó frissítést: <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Az alkalmazás nincs telepítve."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"A csomag telepítését letiltotta a rendszer."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"A nem csomagként telepített alkalmazás ütközik egy már létező csomaggal."</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index 0b46090..8a6f3af 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Հավելվածը տեղադրված է:"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Տեղադրե՞լ այս հավելվածը:"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Թարմացնե՞լ այս հավելվածը։"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Այս հավելվածի թարմացումները ներկայումս կառավարվում են <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>-ի կողմից։\n\nԹարմացնելով՝ դուք հետագայում թարմացումներ կստանաք <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>-ից։"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Այս հավելվածի թարմացումները ներկայումս կառավարվում են <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>-ի կողմից։\n\nՈւզո՞ւմ եք տեղադրել այս թարմացումը <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>-ից։"</string>
<string name="install_failed" msgid="5777824004474125469">"Հավելվածը տեղադրված չէ:"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Փաթեթի տեղադրումն արգելափակվել է:"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Հավելվածը չի տեղադրվել, քանի որ տեղադրման փաթեթն ունի հակասություն առկա փաթեթի հետ:"</string>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
index ed25528..56b47ad 100644
--- a/packages/PackageInstaller/res/values-in/strings.xml
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikasi terinstal."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ingin menginstal aplikasi ini?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ingin mengupdate aplikasi ini?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Update aplikasi ini sekarang dikelola oleh <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nDengan mengupdate, Anda akan mendapatkan update mendatang dari <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Update aplikasi ini sekarang dikelola oleh <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nIngin menginstal update ini dari <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikasi tidak terinstal."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paket diblokir sehingga tidak dapat diinstal."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikasi tidak diinstal karena paket ini bentrok dengan paket yang sudah ada."</string>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
index 0c35415..08fa6a0 100644
--- a/packages/PackageInstaller/res/values-is/strings.xml
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Forritið er uppsett."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Viltu setja upp þetta forrit?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Viltu uppfæra þetta forrit?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> stjórnar uppfærslum þessa forrits eins og er.\n\nMeð því að uppfæra færðu síðari uppfærslur frá <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> í staðinn."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> stjórnar uppfærslum þessa forrits eins og er.\n\nViltu setja upp þessa uppfærslu frá <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Forritið er ekki uppsett."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Lokað var á uppsetningu pakkans."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Forritið var ekki sett upp vegna árekstra á milli pakkans og annars pakka."</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index bfc1e03..939b1f4 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"App installata."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vuoi installare questa app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vuoi aggiornare questa app?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Gli aggiornamenti a questa app sono attualmente gestiti da <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nEseguendo l\'aggiornamento, gli aggiornamenti futuri saranno forniti invece da <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Gli aggiornamenti a questa app sono attualmente gestiti da <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVuoi installare questo aggiornamento da <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"App non installata."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"È stata bloccata l\'installazione del pacchetto."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App non installata poiché il pacchetto è in conflitto con un pacchetto esistente."</string>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 0cd7b30..906043f 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"האפליקציה הותקנה."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"האם ברצונך להתקין אפליקציה זו?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"האם ברצונך לעדכן אפליקציה זו?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"העדכונים של האפליקציה הזו מנוהלים כרגע על ידי <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nלאחר העדכון הנוכחי, העדכונים העתידיים ינוהלו על ידי <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> במקום זאת."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"העדכונים של האפליקציה הזו מנוהלים כרגע על ידי <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nלהתקין את העדכון הזה של <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"האפליקציה לא הותקנה."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"החבילה נחסמה להתקנה."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"האפליקציה לא הותקנה כי החבילה מתנגשת עם חבילה קיימת."</string>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
index 47e295a..d114bb2 100644
--- a/packages/PackageInstaller/res/values-ja/strings.xml
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"アプリをインストールしました。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"このアプリをインストールしますか?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"このアプリを更新しますか?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"このアプリのアップデートは現在 <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> によって管理されています。\n\n更新すると、今後は代わりに <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> からアップデートを入手するようになります。"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"このアプリのアップデートは現在 <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> によって管理されています。\n\nこのアップデートを <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> からインストールしますか?"</string>
<string name="install_failed" msgid="5777824004474125469">"アプリはインストールされていません。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"パッケージのインストールはブロックされています。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"パッケージが既存のパッケージと競合するため、アプリをインストールできませんでした。"</string>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
index 56b79f6..82ca5a7 100644
--- a/packages/PackageInstaller/res/values-ka/strings.xml
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"აპი დაინსტალირებულია."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"გნებავთ ამ აპის დაყენება?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"გსურთ ამ აპის განახლება?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ამ აპის განახლებებს ამჟამად მართავს <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nგანახლებით მომავალ განახლებებს მიიღებთ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>-გან."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ამ აპის განახლებებს ამჟამად მართავს <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nგსურთ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>-გან განახლების ინსტალაცია?"</string>
<string name="install_failed" msgid="5777824004474125469">"აპი დაუინსტალირებელია."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ამ პაკეტის ინსტალაცია დაბლოკილია."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"აპი ვერ დაინსტალირდა, რადგან პაკეტი კონფლიქტშია არსებულ პაკეტთან."</string>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
index d6ce0ce..562898d 100644
--- a/packages/PackageInstaller/res/values-kk/strings.xml
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Қолданба орнатылды."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Бұл қолданбаны орнатқыңыз келе ме?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Бұл қолданбаны жаңартқыңыз келе ме?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Бұл қолданбаны жаңартуды қазір <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> басқарады.\n\nЖаңартсаңыз, келесі жаңартуды <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> беретін болады."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Бұл қолданбаны жаңартуды қазір <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> басқарады.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> беретін жаңартуды пайдаланғыңыз келе ме?"</string>
<string name="install_failed" msgid="5777824004474125469">"Қолданба орнатылмады."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Пакетті орнатуға тыйым салынды."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Жаңа пакет пен бұрыннан бар пакеттің арасында қайшылық туындағандықтан, қолданба орнатылмады."</string>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
index 7efe082..938aa3be 100644
--- a/packages/PackageInstaller/res/values-km/strings.xml
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"បានដំឡើងកម្មវិធី។"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"តើអ្នកចង់ដំឡើងកម្មវិធីនេះដែរទេ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"តើអ្នកចង់ដំឡើងកំណែកម្មវិធីនេះដែរទេ?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"បច្ចុប្បន្ន ការដំឡើងកំណែកម្មវិធីនេះត្រូវបានគ្រប់គ្រងដោយ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>។\n\nតាមរយៈការដំឡើងកំណែ អ្នកនឹងទទួលបានកំណែថ្មីៗនៅពេលក្រោយពី <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ជំនួសវិញ។"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"បច្ចុប្បន្ន ការដំឡើងកំណែកម្មវិធីនេះត្រូវបានគ្រប់គ្រងដោយ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>។\n\nតើអ្នកចង់ដំឡើងកំណែថ្មីនេះពី <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ឬ។"</string>
<string name="install_failed" msgid="5777824004474125469">"មិនបានដំឡើងកម្មវិធីទេ។"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"កញ្ចប់ត្រូវបានទប់ស្កាត់មិនឱ្យដំឡើង។"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"កម្មវិធីមិនបានដំឡើងទេ ដោយសារកញ្ចប់កម្មវិធីមិនត្រូវគ្នាជាមួយកញ្ចប់ដែលមានស្រាប់។"</string>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
index 57a9b64a..e70d75d 100644
--- a/packages/PackageInstaller/res/values-kn/strings.xml
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಬಯಸುವಿರಾ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಬಯಸುವಿರಾ?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ಈ ಆ್ಯಪ್ನ ಅಪ್ಡೇಟ್ಗಳನ್ನು ಪ್ರಸ್ತುತ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ಅವರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ.\n\nಅಪ್ಡೇಟ್ ಮಾಡುವುದರಿಂದ, ಬದಲಿಗೆ ನೀವು ಭವಿಷ್ಯದ ಅಪ್ಡೇಟ್ಗಳನ್ನು <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ಅವರಿಂದ ಪಡೆಯುತ್ತೀರಿ."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ಈ ಆ್ಯಪ್ನ ಅಪ್ಡೇಟ್ಗಳನ್ನು ಪ್ರಸ್ತುತ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ಅವರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ.\n\nನೀವು <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ಅವರ ಈ ಅಪ್ಡೇಟ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಬಯಸುವಿರಾ."</string>
<string name="install_failed" msgid="5777824004474125469">"ಆ್ಯಪ್ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿಲ್ಲ."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ಇನ್ಸ್ಟಾಲ್ ಮಾಡುವ ಪ್ಯಾಕೇಜ್ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ಪ್ಯಾಕೇಜ್ನಂತೆ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿರುವ ಆ್ಯಪ್ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೇಜ್ ಜೊತೆಗೆ ಸಂಘರ್ಷವಾಗುತ್ತದೆ."</string>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
index dbc7bc8..b5adf3f 100644
--- a/packages/PackageInstaller/res/values-ko/strings.xml
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"앱이 설치되었습니다."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"이 앱을 설치하시겠습니까?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"이 앱을 업데이트하시겠습니까?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"이 앱 업데이트는 현재 <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>에서 관리합니다.\n\n업데이트할 경우 대신 <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>에서 향후 업데이트를 제공합니다."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"이 앱 업데이트는 현재 <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>에서 관리합니다.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>에서 제공하는 업데이트를 설치하시겠습니까?"</string>
<string name="install_failed" msgid="5777824004474125469">"앱이 설치되지 않았습니다."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"패키지 설치가 차단되었습니다."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"패키지가 기존 패키지와 충돌하여 앱이 설치되지 않았습니다."</string>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index 614fbaf..34c6293 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Колдонмо орнотулду."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Бул колдонмону орнотоюн деп жатасызбы?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Бул колдонмону жаңыртайын деп жатасызбы?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Бул колдонмонун жаңыртууларын учурда <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> тескеп жатат.\n\nЖаңыртуу менен келечектеги жаңыртууларды анын ордуна <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> жөнөтүп калат."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Бул колдонмонун жаңыртууларын учурда <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> тескеп жатат.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> жөнөткөн жаңыртуунун чыгарып саласызбы?"</string>
<string name="install_failed" msgid="5777824004474125469">"Колдонмо орнотулган жок."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Топтомду орнотууга болбойт."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Башка топтом менен дал келбегендиктен колдонмо орнотулган жок."</string>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
index d8779e7..45cd47a 100644
--- a/packages/PackageInstaller/res/values-lo/strings.xml
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ຕິດຕັ້ງແອັບແລ້ວ."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ທ່ານຕ້ອງການຕິດຕັ້ງແອັບນີ້ບໍ່?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ທ່ານຕ້ອງການອັບເດດແອັບນີ້ບໍ່?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ໃນຕອນນີ້ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ເປັນຜູ້ຈັດການການອັບເດດແອັບນີ້.\n\nຫາກອັບເດດ, ທ່ານຈະໄດ້ຮັບການອັບເດດໃນອະນາຄົດຈາກ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ແທນ."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ໃນຕອນນີ້ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ເປັນຜູ້ຈັດການການອັບເດດແອັບນີ້.\n\nທ່ານຕ້ອງການຕິດຕັ້ງການອັບເດດນີ້ຈາກ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ຫຼືບໍ່."</string>
<string name="install_failed" msgid="5777824004474125469">"ບໍ່ໄດ້ຕິດຕັ້ງແອັບເທື່ອ."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ແພັກເກດຖືກບລັອກບໍ່ໃຫ້ໄດ້ຮັບການຕິດຕັ້ງ."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ບໍ່ໄດ້ຕິດຕັ້ງແອັບເນື່ອງຈາກແພັກເກດຂັດແຍ່ງກັບແພັກເກດທີ່ມີຢູ່ກ່ອນແລ້ວ."</string>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
index 8b011be..b802fa2 100644
--- a/packages/PackageInstaller/res/values-lv/strings.xml
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Lietotne ir instalēta."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vai vēlaties instalēt šo lietotni?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vai vēlaties atjaunināt šo lietotni?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Šīs lietotnes atjauninājumus pašlaik pārvalda <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nJa veiksiet atjaunināšanu, turpmākos atjauninājumus nodrošinās <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Šīs lietotnes atjauninājumus pašlaik pārvalda <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVai vēlaties instalēt atjauninājumu, ko nodrošina <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Lietotne nav instalēta."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakotnes instalēšana tika bloķēta."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Lietotne netika instalēta, jo pastāv pakotnes konflikts ar esošu pakotni."</string>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
index 3c681e9..c038506 100644
--- a/packages/PackageInstaller/res/values-mk/strings.xml
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Апликацијата е инсталирана."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Дали сакате да ја инсталирате апликацијава?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Дали сакате да ја ажурирате апликацијава?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Ажурирањата на апликацијава тековно се управуваат од <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nСо ажурирање, ќе добивате ажурирања во иднината од <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Ажурирањата на апликацијава тековно се управуваат од <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nДали сакате да го инсталирате ажурирањево од <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Апликацијата не е инсталирана."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Инсталирањето на пакетот е блокирано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Апликација што не е инсталирана како пакет е во конфликт со постоечки пакет."</string>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
index aa3aac0..68bb819 100644
--- a/packages/PackageInstaller/res/values-ml/strings.xml
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തു."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ഈ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യണോ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ഈ ആപ്പ് അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ഈ ആപ്പിലെ അപ്ഡേറ്റുകൾ നിലവിൽ മാനേജ് ചെയ്യുന്നത് <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ആണ്.\n\nഅപ്ഡേറ്റ് ചെയ്യുന്നതിലൂടെ, പകരം നിങ്ങൾക്ക് ഭാവി അപ്ഡേറ്റുകൾ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> എന്നയാളിൽ നിന്ന് ലഭിക്കും."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ഈ ആപ്പിലെ അപ്ഡേറ്റുകൾ നിലവിൽ മാനേജ് ചെയ്യുന്നത് <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ആണ്.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> എന്നയാളിൽ നിന്ന് ഈ അപ്ഡേറ്റ് ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങൾ താൽപ്പര്യപ്പെടുന്നുണ്ടോ."</string>
<string name="install_failed" msgid="5777824004474125469">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"പാക്കേജ് ഇൻസ്റ്റാൾ ചെയ്യുന്നത് ബ്ലോക്ക് ചെയ്തു."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"പാക്കേജിന് നിലവിലുള്ള പാക്കേജുമായി പൊരുത്തക്കേടുള്ളതിനാൽ, ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."</string>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
index 112e2f1..6a95f54 100644
--- a/packages/PackageInstaller/res/values-mn/strings.xml
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Аппыг суулгасан."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Та энэ аппыг суулгахыг хүсэж байна уу?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Та энэ аппыг шинэчлэхийг хүсэж байна уу?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Энэ аппын шинэчлэлтийг одоогоор <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>-с удирддаг.\n\nШинэчилснээр та цаашдын шинэчлэлтийг оронд нь <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>-с авна."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Энэ аппын шинэчлэлтийг одоогоор <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>-с удирддаг.\n\nТа <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>-н энэ шинэчлэлтийг суулгахыг хүсэж байна уу?"</string>
<string name="install_failed" msgid="5777824004474125469">"Аппыг суулгаагүй."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Багц суулгахыг блоклосон байна."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Багц одоо байгаа багцтай тохирохгүй байгаа тул аппыг суулгаж чадсангүй."</string>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index 5a79715..fb271b3 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"अॅप इंस्टॉल झाले."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"तुम्हाला हे ॲप इंस्टॉल करायचे आहे का?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"तुम्हाला हे ॲप अपडेट करायचे आहे का?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"या अॅपसाठीचे अपडेट सध्या <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> द्वारे व्यवस्थापित केले जात आहेत.\n\nत्या ऐवजी, अपडेट केल्याने, तुम्हाला भविष्यातील अपडेट <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> कडून मिळतील."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"या अॅपसाठीचे अपडेट सध्या <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> द्वारे व्यवस्थापित केले जात आहेत.\n\nतुम्हाला हे अपडेट <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> कडून इंस्टॉल करायचे आहे का."</string>
<string name="install_failed" msgid="5777824004474125469">"अॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"पॅकेज इंस्टॉल होण्यापासून ब्लॉक केले होते."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल झाले नाही."</string>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
index 8d5e25f..4e9daa7 100644
--- a/packages/PackageInstaller/res/values-ms/strings.xml
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikasi dipasang."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Adakah anda ingin memasang aplikasi ini?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Adakah anda mahu mengemas kini apl ini?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Kemaskinian pada apl ini diuruskan oleh <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> pada masa ini.\n\nDengan membuat pengemaskinian, anda akan mendapat kemaskinian akan datang daripada <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Kemaskinian pada apl ini diuruskan oleh <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> pada masa ini.\n\nAdakah anda mahu memasang kemaskinian ini daripada <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikasi tidak dipasang."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakej ini telah disekat daripada dipasang."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Apl tidak dipasang kerana pakej bercanggah dengan pakej yang sedia ada."</string>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
index a9ac033..35e82fe 100644
--- a/packages/PackageInstaller/res/values-my/strings.xml
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"အက်ပ်ထည့်သွင်းပြီးပါပြီ။"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ဤအက်ပ်ကို ထည့်သွင်းလိုသလား။"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ဤအက်ပ်ကို အပ်ဒိတ်လုပ်လိုသလား။"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ဤအက်ပ်ရှိ အပ်ဒိတ်များကို လောလောဆယ်တွင် <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> က စီမံထားသည်။\n\n၎င်းအစား အပ်ဒိတ်လုပ်ခြင်းဖြင့် <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ထံမှ နောက်အပ်ဒိတ်များ ရရှိပါမည်။"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ဤအက်ပ်ရှိ အပ်ဒိတ်များကို လောလောဆယ်တွင် <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> က စီမံထားသည်။\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ထံမှ ဤအပ်ဒိတ်ကို ထည့်သွင်းလိုပါသလား။"</string>
<string name="install_failed" msgid="5777824004474125469">"အက်ပ်မထည့်သွင်းရသေးပါ"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ပက်ကေ့ဂျ်ထည့်သွင်းခြင်းကို ပိတ်ထားသည်။"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ပက်ကေ့ဂျ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် လက်ရှိပက်ကေ့ဂျ်နှင့် တိုက်နေသည်။"</string>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
index 5f70b9d..3b145d1 100644
--- a/packages/PackageInstaller/res/values-nb/strings.xml
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Appen er installert."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vil du installere denne appen?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vil du oppdatere denne appen?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Oppdateringer for denne appen administreres nå av <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVed å oppdatere får du fremtidige oppdateringer fra <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> i stedet."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Oppdateringer for denne appen administreres nå av <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVil du installere denne oppdateringen fra <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Appen ble ikke installert."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakken er blokkert fra å bli installert."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen ble ikke installert fordi pakken er i konflikt med en eksisterende pakke."</string>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index e7e03a4..4efd35a 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"एप इन्स्टल गरियो।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"तपाईं यो एप इन्स्टल गर्न चाहनुहुन्छ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"तपाईं यो एप अपडेट गर्न चाहनुहुन्छ?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ले हाल यो एपका अपडेटहरू व्यवस्थापन गर्छ।\n\nतपाईंले अपडेट गर्नुभयो भने तपाईं भविष्यमा <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> बाट अपडेटहरू प्राप्त गर्नु हुने छ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ले हाल यो एपका अपडेटहरू व्यवस्थापन गर्छ।\n\nतपाईं <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ले उपलब्ध गराएको यो अपडेट इन्स्टल गर्न चाहनुहुन्छ?"</string>
<string name="install_failed" msgid="5777824004474125469">"एप स्थापना गरिएन।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"यो प्याकेज स्थापना गर्ने क्रममा अवरोध गरियो।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"प्याकेजका रूपमा स्थापना नगरिएको एप विद्यमान प्याकेजसँग मेल खाँदैन।"</string>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index a859861..3e85971 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"App geïnstalleerd."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Wil je deze app installeren?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Wil je deze app updaten?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Updates voor deze app worden momenteel beheerd door <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nAls je updatet, krijg je volgende updates in plaats daarvan van <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Updates voor deze app worden momenteel beheerd door <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nWil je deze update van <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> installeren?"</string>
<string name="install_failed" msgid="5777824004474125469">"App niet geïnstalleerd."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"De installatie van het pakket is geblokkeerd."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App die niet is geïnstalleerd als pakket conflicteert met een bestaand pakket."</string>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
index 5131a11..f457a2d 100644
--- a/packages/PackageInstaller/res/values-or/strings.xml
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ଆପ ଇନଷ୍ଟଲ ହୋଇଗଲା।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ଆପଣ ଏହି ଆପକୁ ଇନଷ୍ଟଲ୍ କରିବା ପାଇଁ ଚାହୁଁଛନ୍ତି କି?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ଆପଣ ଏହି ଆପକୁ ଅପଡେଟ୍ କରିବା ପାଇଁ ଚାହୁଁଛନ୍ତି କି?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ଏହି ଆପରେ ଅପଡେଟଗୁଡ଼ିକ ବର୍ତ୍ତମାନ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ ହେଉଛି।\n\nଅପଡେଟ କରି, ଆପଣ ଏହା ପରିବର୍ତ୍ତେ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>ରୁ ଭବିଷ୍ୟତର ଅପଡେଟଗୁଡ଼ିକ ପାଇବେ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ଏହି ଆପରେ ଅପଡେଟଗୁଡ଼ିକ ବର୍ତ୍ତମାନ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ ହେଉଛି।\n\nଆପଣ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>ରୁ ଏହି ଅପଡେଟ ଇନଷ୍ଟଲ କରିବାକୁ ଚାହାଁନ୍ତି?"</string>
<string name="install_failed" msgid="5777824004474125469">"ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇନାହିଁ।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ଏହି ପ୍ୟାକେଜ୍କୁ ଇନଷ୍ଟଲ୍ କରାଯିବାରୁ ଅବରୋଧ କରାଯାଇଥିଲା।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ପୂର୍ବରୁ ଥିବା ପ୍ୟାକେଜ୍ ସହ ଏହି ପ୍ୟାକେଜ୍ର ସମସ୍ୟା ଉପୁଯିବାରୁ ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇପାରିଲା ନାହିଁ।"</string>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index 34373f9..e8977d1 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ਐਪ ਸਥਾਪਤ ਕੀਤੀ ਗਈ।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ਫ਼ਿਲਹਾਲ ਇਸ ਐਪ ਦੇ ਅੱਪਡੇਟਾਂ ਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।\n\nਅੱਪਡੇਟ ਕਰਨ ਨਾਲ, ਇਸਦੀ ਬਜਾਏ ਤੁਹਾਨੂੰ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ਤੋਂ ਭਵਿੱਖੀ ਅੱਪਡੇਟ ਪ੍ਰਾਪਤ ਹੋਣਗੇ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ਫ਼ਿਲਹਾਲ ਇਸ ਐਪ ਦੇ ਅੱਪਡੇਟਾਂ ਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।\n\nਕੀ ਤੁਸੀਂ <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ਵੱਲੋਂ ਇਸ ਅੱਪਡੇਟ ਨੂੰ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ।"</string>
<string name="install_failed" msgid="5777824004474125469">"ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ਪੈਕੇਜ ਨੂੰ ਸਥਾਪਤ ਹੋਣ ਤੋਂ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਸੀ।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ਪੈਕੇਜ ਦੇ ਇੱਕ ਮੌਜੂਦਾ ਪੈਕੇਜ ਨਾਲ ਵਿਵਾਦ ਹੋਣ ਕਰਕੇ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index 6626f39..e06dea6 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacja została zainstalowana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Zainstalować tę aplikację?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Zaktualizować tę aplikację?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Aktualizacjami tej aplikacji zarządza obecnie <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nJeśli pobierzesz aktualizację, przyszłe aktualizacje będzie zapewniać <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Aktualizacjami tej aplikacji zarządza obecnie <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nChcesz zainstalować tę aktualizację ze źródła <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacja nie została zainstalowana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalacja pakietu została zablokowana."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacja nie została zainstalowana, bo powoduje konflikt z istniejącym pakietem."</string>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index 51f94e2..98a75cf 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"App instalado."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Quer instalar esse app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Quer atualizar esse app?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"No momento, as atualizações deste app são gerenciadas por <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nAo atualizar, você receberá as atualizações futuras de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"No momento, as atualizações deste app são gerenciadas por <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nGostaria de instalar esta atualização de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"O app não foi instalado."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"A instalação do pacote foi bloqueada."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index 51f94e2..98a75cf 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"App instalado."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Quer instalar esse app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Quer atualizar esse app?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"No momento, as atualizações deste app são gerenciadas por <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nAo atualizar, você receberá as atualizações futuras de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"No momento, as atualizações deste app são gerenciadas por <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nGostaria de instalar esta atualização de <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"O app não foi instalado."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"A instalação do pacote foi bloqueada."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index 0de0ac0..7a18b2a 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplicație instalată."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vrei să instalezi această aplicație?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vrei să actualizezi această aplicație?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Actualizările acestei aplicații sunt gestionate de <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nDacă actualizezi, vei primi actualizări de la <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Actualizările acestei aplicații sunt gestionate de <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVrei să instalezi această actualizare de la <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplicația nu a fost instalată."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalarea pachetului a fost blocată."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplicația nu a fost instalată deoarece pachetul intră în conflict cu un pachet existent."</string>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
index 106d325..3bc40c3 100644
--- a/packages/PackageInstaller/res/values-ru/strings.xml
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Приложение установлено."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Установить приложение?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Обновить приложение?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"В настоящее время обновлениями этого приложения управляет <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nЕсли вы установите обновление, то все последующие будете получать от <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"В настоящее время обновлениями этого приложения управляет <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nВы хотите установить это обновление от <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Приложение не установлено."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Установка пакета заблокирована."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Приложение не установлено, так как оно конфликтует с другим пакетом."</string>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
index e2880eb..9d400ef 100644
--- a/packages/PackageInstaller/res/values-si/strings.xml
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"යෙදුම ස්ථාපනය කර ඇත."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"මෙම යෙදුම ස්ථාපනය කිරීමට ඔබට අවශ්යද?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ඔබට මෙම යෙදුම යාවත්කාලීන කිරීමට අවශ්යද?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"මෙම යෙදුම වෙත යාවත්කාලීන කිරීම් දැනට <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> විසින් කළමනාකරණය කරනු ලැබේ.\n\nයාවත්කාලීන කිරීමෙන්, ඔබට <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> වෙතින් අනාගත යාවත්කාලීන ලැබෙනු ඇත."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"මෙම යෙදුම වෙත යාවත්කාලීන කිරීම් දැනට <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> විසින් කළමනාකරණය කරනු ලැබේ.\n\nඔබට <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> වෙතින් මෙම යාවත්කාලීනය ස්ථාපනය කිරීමට අවශ්ය ද?"</string>
<string name="install_failed" msgid="5777824004474125469">"යෙදුම ස්ථාපනය කර නැත."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"මෙම පැකේජය ස්ථාපනය කිරීම අවහිර කරන ලදි."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"පැකේජය දැනට පවතින පැකේජයක් සමග ගැටෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."</string>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
index b5ecf78..cb3cdae 100644
--- a/packages/PackageInstaller/res/values-sk/strings.xml
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikácia bola nainštalovaná."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Chcete túto aplikáciu nainštalovať?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Chcete túto aplikáciu aktualizovať?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Aktualizácie tejto aplikácie momentálne spravuje vlastník <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nPo aktualizovaní budete namiesto toho získavať budúce aplikácie od vlastníka <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Aktualizácie tejto aplikácie momentálne spravuje vlastník <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nChcete túto aktualizáciu nainštalovať od vlastníka <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikácia nebola nainštalovaná."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Inštalácia balíka bola zablokovaná."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikácia sa nenainštalovala, pretože balík je v konflikte s existujúcim balíkom."</string>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
index fe4e717..654fba1 100644
--- a/packages/PackageInstaller/res/values-sl/strings.xml
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je nameščena."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ali želite namestiti to aplikacijo?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ali želite posodobiti to aplikacijo?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Posodobitve te aplikacije trenutno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nČe posodobite, bo pošiljatelj prihodnjih posodobitev <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Posodobitve te aplikacije trenutno upravlja <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nAli želite namestiti to posodobitev, ki jo je poslal <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija ni nameščena."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Namestitev paketa je bila blokirana."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija ni bila nameščena, ker je paket v navzkrižju z obstoječim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
index b52bccd..b967259 100644
--- a/packages/PackageInstaller/res/values-sq/strings.xml
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacioni u instalua."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Dëshiron ta instalosh këtë aplikacion?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Dëshiron ta përditësosh këtë aplikacion?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Përditësimet për këtë aplikacion menaxhohen aktualisht nga <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nDuke e përditësuar, përditësimet në të ardhmen do t\'i marrësh nga <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Përditësimet për këtë aplikacion menaxhohen aktualisht nga <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nDëshiron ta instalosh këtë përditësim nga <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacioni nuk u instalua."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalimi paketës u bllokua."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacioni nuk u instalua pasi paketa është në konflikt me një paketë ekzistuese."</string>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
index 19c77ce..8858ef5 100644
--- a/packages/PackageInstaller/res/values-sr/strings.xml
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Апликација је инсталирана."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Желите да инсталирате ову апликацију?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Желите да ажурирате ову апликацију?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Ажурирањима ове апликације тренутно управља <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nАко ажурирате, будућа ажурирања ћете добијати од власника <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Ажурирањима ове апликације тренутно управља <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nЖелите ли да инсталирате ово ажурирање власника <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Апликација није инсталирана."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Инсталирање пакета је блокирано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Апликација није инсталирана јер је пакет неусаглашен са постојећим пакетом."</string>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index 351e3f8..43f3d4c 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Appen har installerats."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vill du installera den här appen?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vill du uppdatera den här appen?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Uppdateringar av den här appen hanteras för närvarande av <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nNär du uppdaterar får du i stället uppdateringar från <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Uppdateringar av den här appen hanteras för närvarande av <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nVill du installera den här uppdateringen från <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Appen har inte installerats."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketet har blockerats för installation."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen har inte installerats på grund av en konflikt mellan detta paket och ett befintligt paket."</string>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
index b7ffc29..978f667 100644
--- a/packages/PackageInstaller/res/values-sw/strings.xml
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Imesakinisha programu."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ungependa kusakinisha programu hii?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ungependa kusasisha programu hii?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Masasisho ya programu hii kwa sasa yanadhibitiwa na <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nKwa kusasisha, utapata masasisho yajayo kutoka kwa <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> badala yake."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Masasisho ya programu hii kwa sasa yanasimamiwa na <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>\n\nJe, ungependa kusakinisha sasisho hili kutoka kwa <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Imeshindwa kusakinisha programu."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Kifurushi kimezuiwa kisisakinishwe."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Programu haikusakinishwa kwa sababu kifurushi kinakinzana na kifurushi kingine kilichopo."</string>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
index ad0c9a0..eaa8711 100644
--- a/packages/PackageInstaller/res/values-ta/strings.xml
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ஆப்ஸ் நிறுவப்பட்டது."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"இந்த ஆப்ஸை நிறுவ வேண்டுமா?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"இந்த ஆப்ஸைப் புதுப்பிக்க வேண்டுமா?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"இந்த ஆப்ஸுக்கான புதுப்பிப்புகள் தற்போது <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> மூலம் நிர்வகிக்கப்படுகின்றன.\n\nபுதுப்பிப்பதன் மூலம் இனிவரும் புதுப்பிப்புகளை <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> மூலம் பெறுவீர்கள்."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"இந்த ஆப்ஸுக்கான புதுப்பிப்புகள் தற்போது <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> மூலம் நிர்வகிக்கப்படுகின்றன.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> மூலம் இந்தப் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா?"</string>
<string name="install_failed" msgid="5777824004474125469">"ஆப்ஸ் நிறுவப்படவில்லை."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"இந்தத் தொகுப்பு நிறுவப்படுவதிலிருந்து தடுக்கப்பட்டது."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"இந்தத் தொகுப்பு ஏற்கனவே உள்ள தொகுப்புடன் முரண்படுவதால் ஆப்ஸ் நிறுவப்படவில்லை."</string>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index 6ab13dc..a6cd3e7 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"యాప్ ఇన్స్టాల్ చేయబడింది."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"మీరు ఈ యాప్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"మీరు ఈ యాప్ను అప్డేట్ చేయాలనుకుంటున్నారా?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ఈ యాప్కి అప్డేట్లు ప్రస్తుతం <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ద్వారా మేనేజ్ చేయబడుతున్నాయి.\n\nఅప్డేట్ చేయడం ద్వారా, మీరు అందుకు బదులుగా <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> నుండి భవిష్యత్తు అప్డేట్లను పొందుతారు."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ఈ యాప్కి అప్డేట్లు ప్రస్తుతం<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ద్వారా మేనేజ్ చేయబడుతున్నాయి.\n\nమీరు <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> నుండి ఈ అప్డేట్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా."</string>
<string name="install_failed" msgid="5777824004474125469">"యాప్ ఇన్స్టాల్ చేయబడలేదు."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ప్యాకేజీ ఇన్స్టాల్ కాకుండా బ్లాక్ చేయబడింది."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ప్యాకేజీ, అలాగే ఇప్పటికే ఉన్న ప్యాకేజీ మధ్య వైరుధ్యం ఉన్నందున యాప్ ఇన్స్టాల్ చేయబడలేదు."</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index 0e7274b..fd1af0b 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"ติดตั้งแอปแล้ว"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"คุณต้องการติดตั้งแอปนี้ไหม"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"คุณต้องการอัปเดตแอปนี้ไหม"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"ขณะนี้ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> เป็นผู้จัดการการอัปเดตแอปนี้\n\nหากอัปเดต คุณจะได้รับการอัปเดตในอนาคตจาก <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> แทน"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"ขณะนี้ <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> เป็นผู้จัดการการอัปเดตแอปนี้\n\nคุณต้องการติดตั้งการอัปเดตนี้จาก <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> ไหม"</string>
<string name="install_failed" msgid="5777824004474125469">"ไม่ได้ติดตั้งแอป"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"มีการบล็อกแพ็กเกจไม่ให้ติดตั้ง"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ไม่ได้ติดตั้งแอปเพราะแพ็กเกจขัดแย้งกับแพ็กเกจที่มีอยู่"</string>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
index 5f480bc..f46be7f 100644
--- a/packages/PackageInstaller/res/values-tl/strings.xml
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Na-install na ang app."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Gusto mo bang i-install ang app na ito?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Gusto mo bang i-update ang app na ito?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Kasalukuyang pinamamahalaan ng <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ang mga update sa app na ito.\n\nSa pamamagitan ng pag-update, makakakuha ka na lang ng mga update sa hinaharap mula sa <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Kasalukuyang pinamamahalaan ng <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> ang mga update sa app na ito.\n\nGusto mo bang i-install ang update na ito mula sa <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Hindi na-install ang app."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Na-block ang pag-install sa package."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Hindi na-install ang app dahil nagkakaproblema ang package sa isang dati nang package."</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 7eacad0..7548c5d 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Uygulama yüklendi."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Bu uygulamayı yüklemek istiyor musunuz?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Bu uygulamayı güncellemek istiyor musunuz?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Bu uygulamadaki güncellemeler şu anda <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> tarafından yönetiliyor.\n\nUygulamayı güncellerseniz bundan sonraki güncellemeleri <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> gönderecektir."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Bu uygulamadaki güncellemeler şu anda <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> tarafından yönetiliyor.\n\n<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> tarafından gönderilen bu güncellemeyi yüklemek istiyor musunuz?"</string>
<string name="install_failed" msgid="5777824004474125469">"Uygulama yüklenmedi."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketin yüklemesi engellendi."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Paket, mevcut bir paketle çakıştığından uygulama yüklenemedi."</string>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
index e94c716..48ff554 100644
--- a/packages/PackageInstaller/res/values-uk/strings.xml
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Програму встановлено."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Установити цей додаток?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Оновити цей додаток?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Оновленнями для цього додатка наразі керує <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nОновивши його зараз, ви надалі отримуватимете оновлення від <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Оновленнями для цього додатка наразі керує <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nУстановити це оновлення від <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>?"</string>
<string name="install_failed" msgid="5777824004474125469">"Програму не встановлено."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Встановлення пакета заблоковано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Додаток не встановлено, оскільки пакет конфліктує з наявним пакетом."</string>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
index 234cd2a..2d49c9b 100644
--- a/packages/PackageInstaller/res/values-vi/strings.xml
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Ứng dụng đã được cài đặt."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Bạn có muốn cài đặt ứng dụng này không?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Bạn có muốn cập nhật ứng dụng này không?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Hiện tại, <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> đang quản lý các bản cập nhật của ứng dụng này.\n\nBằng việc cập nhật, bạn sẽ nhận được các bản cập nhật sau này của <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Hiện tại, <xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g> đang quản lý các bản cập nhật của ứng dụng này.\n\nBạn có muốn cài đặt bản cập nhật này của <xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> không?"</string>
<string name="install_failed" msgid="5777824004474125469">"Ứng dụng chưa được cài đặt."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Đã chặn cài đặt gói."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Chưa cài đặt được ứng dụng do gói xung đột với một gói hiện có."</string>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
index 737bf23..eb13321 100644
--- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"已安装应用。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"要安装此应用吗?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"要更新此应用吗?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"此应用的更新目前由<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>管理。\n\n更新后,将改由<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>向您提供日后的更新。"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"此应用的更新目前由<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>管理。\n\n要安装这个来自<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>的更新吗?"</string>
<string name="install_failed" msgid="5777824004474125469">"未安装应用。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"系统已禁止安装该软件包。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"应用未安装:软件包与现有软件包存在冲突。"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
index 0ed0bd7..d7fb07e 100644
--- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"已安裝應用程式。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"要安裝此應用程式嗎?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"要更新此應用程式嗎?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"此應用程式的更新目前由「<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>」管理。\n\n更新後將改由「<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>」提供更新。"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"此應用程式的更新目前由「<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>」管理。\n\n是否要安裝這項由「<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>」提供的更新?"</string>
<string name="install_failed" msgid="5777824004474125469">"未安裝應用程式。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"套件已遭封鎖,無法安裝。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"套件與現有的套件發生衝突,無法安裝應用程式。"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
index 9dfb3fd..a825d20 100644
--- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"已安裝應用程式。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"要安裝這個應用程式嗎?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"要更新這個應用程式嗎?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"這個應用程式的更新目前是由「<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>」管理。\n\n更新後,將改由「<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>」提供更新。"</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"這個應用程式的更新目前是由「<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>」管理。\n\n是否要安裝「<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>」提供的這項更新?"</string>
<string name="install_failed" msgid="5777824004474125469">"未安裝應用程式。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"系統已封鎖這個套件,因此無法安裝。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"應用程式套件與現有套件衝突,因此未能完成安裝。"</string>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index f5e6b75..62c8a9a 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -26,10 +26,8 @@
<string name="install_done" msgid="5987363587661783896">"Uhlelo lokusebenza olufakiwe."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ingabe ufuna ukufaka le app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ingabe ufuna ukubuyekeza le app?"</string>
- <!-- no translation found for install_confirm_question_update_owner_changed (4215696609006069124) -->
- <skip />
- <!-- no translation found for install_confirm_question_update_owner_reminder (8778026710268011466) -->
- <skip />
+ <string name="install_confirm_question_update_owner_changed" msgid="4215696609006069124">"Izibuyekezo zale app okwamanje ziphethwe yi-<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nNgokubuyekeza, kunalokho uzothola izibuyekezo zesikhathi esizayo ezivela ku-<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g> esikhundleni."</string>
+ <string name="install_confirm_question_update_owner_reminder" msgid="8778026710268011466">"Izibuyekezo zale app okwamanje ziphethwe yi-<xliff:g id="EXISTING_UPDATE_OWNER">%1$s</xliff:g>.\n\nUyafuna ukufaka lesi sibuyekezo esivela ku-<xliff:g id="NEW_UPDATE_OWNER">%2$s</xliff:g>."</string>
<string name="install_failed" msgid="5777824004474125469">"Uhlelo lokusebenza alufakiwe."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Iphakheji livinjiwe kusukela ekufakweni."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Uhlelo lokusebenza alufakiwe njengoba ukuphakheja kushayisana nephakheji elikhona."</string>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index cb2baa9..ae6f71c 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -143,6 +143,8 @@
<!-- [CHAR LIMIT=100] -->
<string name="uninstall_done_app">Uninstalled <xliff:g id="package_label">%1$s</xliff:g></string>
<!-- [CHAR LIMIT=100] -->
+ <string name="uninstall_done_clone_app">Deleted <xliff:g id="package_label">%1$s</xliff:g> clone</string>
+ <!-- [CHAR LIMIT=100] -->
<string name="uninstall_failed">Uninstall unsuccessful.</string>
<!-- [CHAR LIMIT=100] -->
<string name="uninstall_failed_app">Uninstalling <xliff:g id="package_label">%1$s</xliff:g> unsuccessful.</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
index b9552fc..e089aef 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -49,6 +49,7 @@
static final String EXTRA_UNINSTALL_ID = "com.android.packageinstaller.extra.UNINSTALL_ID";
static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+ static final String EXTRA_IS_CLONE_APP = "com.android.packageinstaller.extra.IS_CLONE_APP";
@Override
public void onReceive(Context context, Intent intent) {
@@ -84,7 +85,10 @@
case PackageInstaller.STATUS_SUCCESS:
notificationManager.cancel(uninstallId);
- Toast.makeText(context, context.getString(R.string.uninstall_done_app, appLabel),
+ boolean isCloneApp = intent.getBooleanExtra(EXTRA_IS_CLONE_APP, false);
+ Toast.makeText(context, isCloneApp
+ ? context.getString(R.string.uninstall_done_clone_app, appLabel)
+ : context.getString(R.string.uninstall_done_app, appLabel),
Toast.LENGTH_LONG).show();
return;
case PackageInstaller.STATUS_FAILURE_BLOCKED: {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 04496b9..7250bdd 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -67,6 +67,7 @@
private static final String TAG = "UninstallerActivity";
private static final String UNINSTALLING_CHANNEL = "uninstalling";
+ private boolean mIsClonedApp;
public static class DialogInfo {
public ApplicationInfo appInfo;
@@ -277,6 +278,14 @@
fragment.show(ft, "dialog");
}
+ /**
+ * Starts uninstall of app.
+ */
+ public void startUninstallProgress(boolean keepData, boolean isClonedApp) {
+ mIsClonedApp = isClonedApp;
+ startUninstallProgress(keepData);
+ }
+
public void startUninstallProgress(boolean keepData) {
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
@@ -329,6 +338,7 @@
broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
+ broadcastIntent.putExtra(UninstallFinish.EXTRA_IS_CLONE_APP, mIsClonedApp);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(this, uninstallId, broadcastIntent,
@@ -343,7 +353,10 @@
Notification uninstallingNotification =
(new Notification.Builder(this, UNINSTALLING_CHANNEL))
.setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true)
- .setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true)
+ .setContentTitle(mIsClonedApp
+ ? getString(R.string.uninstalling_cloned_app, label)
+ : getString(R.string.uninstalling_app, label))
+ .setOngoing(true)
.build();
notificationManager.notify(uninstallId, uninstallingNotification);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 1bbdad5..4a93bf8 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -54,6 +54,7 @@
private static final String LOG_TAG = UninstallAlertDialogFragment.class.getSimpleName();
private @Nullable CheckBox mKeepData;
+ private boolean mIsClonedApp;
/**
* Get number of bytes of the app data of the package.
@@ -125,7 +126,6 @@
messageBuilder.append(" ").append(appLabel).append(".\n\n");
}
}
- boolean isClonedApp = false;
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
@@ -154,7 +154,7 @@
userName));
} else if (customUserManager.isUserOfType(USER_TYPE_PROFILE_CLONE)
&& customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) {
- isClonedApp = true;
+ mIsClonedApp = true;
messageBuilder.append(getString(
R.string.uninstall_application_text_current_user_clone_profile));
} else {
@@ -162,7 +162,7 @@
getString(R.string.uninstall_application_text_user, userName));
}
} else if (isCloneProfile(myUserHandle)) {
- isClonedApp = true;
+ mIsClonedApp = true;
messageBuilder.append(getString(
R.string.uninstall_application_text_current_user_clone_profile));
} else {
@@ -177,7 +177,7 @@
}
}
- if (isClonedApp) {
+ if (mIsClonedApp) {
dialogBuilder.setTitle(getString(R.string.cloned_app_label, appLabel));
} else {
dialogBuilder.setTitle(appLabel);
@@ -236,7 +236,7 @@
UserManager userManager = getContext().getSystemService(UserManager.class);
List<UserHandle> profiles = userManager.getUserProfiles();
for (UserHandle userHandle : profiles) {
- if (!Process.myUserHandle().equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
+ if (!userHandle.equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
cloneUser = userHandle;
break;
}
@@ -260,7 +260,7 @@
public void onClick(DialogInterface dialog, int which) {
if (which == Dialog.BUTTON_POSITIVE) {
((UninstallerActivity) getActivity()).startUninstallProgress(
- mKeepData != null && mKeepData.isChecked());
+ mKeepData != null && mKeepData.isChecked(), mIsClonedApp);
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
}
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml
index b5a70d0..595e46d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Toegelaat"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nie toegelaat nie"</string>
<string name="version_text" msgid="4001669804596458577">"weergawe <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml
index 3cc6e89..e2a1bf5 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"ይፈቀዳል"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"አይፈቀድም"</string>
<string name="version_text" msgid="4001669804596458577">"ሥሪት <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"የተባዛ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
index 2e0a9ef..9a8e7dd 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"مسموح به"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"غير مسموح به"</string>
<string name="version_text" msgid="4001669804596458577">"الإصدار <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"نسخة طبق الأصل من \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml
index dd27aa1..2e8140e 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"অনুমতি দিয়া হৈছে"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"অনুমতি নাই"</string>
<string name="version_text" msgid="4001669804596458577">"সংস্কৰণ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ৰ ক্ল’ন"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml
index 1fc9c36..c6518c8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"İcazə verildi"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"İcazə verilməyib"</string>
<string name="version_text" msgid="4001669804596458577">"versiya <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kopyalanması"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml
index 3d5caaf..f874c6d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dozvoljeno"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nije dozvoljeno"</string>
<string name="version_text" msgid="4001669804596458577">"verzija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml
index f3c3dd0..e3d3926 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Дазволена"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Забаронена"</string>
<string name="version_text" msgid="4001669804596458577">"версія <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml
index c16bea8..0f51436 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Разрешено"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Не е разрешено"</string>
<string name="version_text" msgid="4001669804596458577">"версия <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Копие на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml
index a72570f..71b3239 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"অনুমোদিত"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"অননুমোদিত"</string>
<string name="version_text" msgid="4001669804596458577">"ভার্সন <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ক্লোন"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml
index 9b98057..35983f6 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dozvoljeno"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nije dozvoljeno"</string>
<string name="version_text" msgid="4001669804596458577">"verzija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Kloniranje paketa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml
index 111abe3..70604be 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Amb permís"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Sense permís"</string>
<string name="version_text" msgid="4001669804596458577">"versió <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml
index f58e9c4..5a3e043 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Povoleno"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nepovoleno"</string>
<string name="version_text" msgid="4001669804596458577">"verze <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon balíčku <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml
index 32ae008..c53441c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Tilladt"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ikke tilladt"</string>
<string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml
index 401d8c9..b10f020 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Zulässig"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nicht zulässig"</string>
<string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-Klon"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml
index ad751f5..ac4106a 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Επιτρέπεται"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Δεν επιτρέπεται"</string>
<string name="version_text" msgid="4001669804596458577">"έκδοση <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Κλώνος <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml
index b151c95..a0772f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Allowed"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Not allowed"</string>
<string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml
index b151c95..a0772f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Allowed"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Not allowed"</string>
<string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml
index b151c95..a0772f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Allowed"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Not allowed"</string>
<string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml
index b08a73f..08da5f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"No se permite"</string>
<string name="version_text" msgid="4001669804596458577">"versión <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml
index 45da42c..32ba2f9 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"No permitida"</string>
<string name="version_text" msgid="4001669804596458577">"versión <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml
index d8f43d8..a216abc 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Lubatud"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Pole lubatud"</string>
<string name="version_text" msgid="4001669804596458577">"versioon <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Üksuse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kloon"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml
index 6505096..798cf35 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Baimena dauka"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ez dauka baimenik"</string>
<string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> bertsioa"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> aplikazioaren klona"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
index 616cf87..8654c64 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"مجاز"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"غیرمجاز"</string>
<string name="version_text" msgid="4001669804596458577">"نسخه <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"همتای <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml
index 161f2ae..8f42d50 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Sallittu"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ei sallittu"</string>
<string name="version_text" msgid="4001669804596458577">"versio <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klooni"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
index 5fd70cc..a271ff5 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Autorisé"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non autorisée"</string>
<string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml
index 239a86a..30f2bcd 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Autorisé"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non autorisé"</string>
<string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml
index 809d24d..a98e809 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non permitida"</string>
<string name="version_text" msgid="4001669804596458577">"versión <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml
index a1de2fb..7bff012 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"મંજૂરી છે"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"મંજૂરી નથી"</string>
<string name="version_text" msgid="4001669804596458577">"વર્ઝન <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ની ક્લોન"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml
index 9af4a02..1a962f5 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"अनुमति है"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"अनुमति नहीं है"</string>
<string name="version_text" msgid="4001669804596458577">"वर्शन <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> का क्लोन"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml
index 8556cfb..d179415 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dopušteno"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nije dopušteno"</string>
<string name="version_text" msgid="4001669804596458577">"verzija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml
index 9d928e9..4ccc59f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Engedélyezve"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nem engedélyezett"</string>
<string name="version_text" msgid="4001669804596458577">"verzió: <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klónja"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml
index 064d9998..67b3e73 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Թույլատրված է"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Արգելված է"</string>
<string name="version_text" msgid="4001669804596458577">"տարբերակ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"«<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>» հավելվածի կլոն"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
index 8799c9b..a2a4289c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Diizinkan"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Tidak diizinkan"</string>
<string name="version_text" msgid="4001669804596458577">"versi <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml
index ff7ee6a..5c21bec 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Leyft"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ekki leyft"</string>
<string name="version_text" msgid="4001669804596458577">"útgáfa <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Afrit af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml
index 5d67307..c8cbe76 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Consentita"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non consentita"</string>
<string name="version_text" msgid="4001669804596458577">"versione <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone di <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml
index 4ab76fe..49b6b1f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"יש הרשאה"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"אין הרשאה"</string>
<string name="version_text" msgid="4001669804596458577">"גרסה <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"שכפול של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml
index 1c0d8d3..2f15ab4 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"許可"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"許可しない"</string>
<string name="version_text" msgid="4001669804596458577">"バージョン <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> のクローン"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml
index a4c6783..502fcb1 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"დაშვებულია"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"დაუშვებელია"</string>
<string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> ვერსია"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> კლონი"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml
index 4760d47..1b5e8ed 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Рұқсат етілген"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Рұқсат етілмеген"</string>
<string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> нұсқасы"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клоны"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml
index 962fb5c..2eeb5ce 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"បានអនុញ្ញាត"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"មិនអនុញ្ញាតទេ"</string>
<string name="version_text" msgid="4001669804596458577">"កំណែ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"ក្លូន <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml
index 7edec75..04e7396 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"ಅನುಮತಿಸಲಾಗಿದೆ"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
<string name="version_text" msgid="4001669804596458577">"ಆವೃತ್ತಿ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಕ್ಲೋನ್"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml
index 446580b..ef4ee0d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"허용됨"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"허용되지 않음"</string>
<string name="version_text" msgid="4001669804596458577">"버전 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 클론"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml
index 2596b93..26fe636 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Уруксат берилген"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Тыюу салынган"</string>
<string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> версиясы"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клону"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml
index 0dc64a6..e707c9d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"ອະນຸຍາດແລ້ວ"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ບໍ່ໄດ້ອະນຸຍາດ"</string>
<string name="version_text" msgid="4001669804596458577">"ເວີຊັນ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"ໂຄລນ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml
index 4ca01dc..8bcee5f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Leidžiama"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Neleidžiama"</string>
<string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> versija"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"„<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“ kopija"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml
index bf300ac..6d5017c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Atļauja piešķirta"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Atļauja nav piešķirta"</string>
<string name="version_text" msgid="4001669804596458577">"versija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Lietotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klons"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml
index 839e4c7..56ed2d9 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Со дозволен пристап"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Без дозволен пристап"</string>
<string name="version_text" msgid="4001669804596458577">"верзија <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml
index 0aad964..1090690 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"അനുവാദം ലഭിച്ചു"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"അനുവാദം ലഭിച്ചില്ല"</string>
<string name="version_text" msgid="4001669804596458577">"പതിപ്പ് <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ക്ലോൺ ചെയ്യൽ"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml
index bc6e9ae..2074222 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Зөвшөөрсөн"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Зөвшөөрөөгүй"</string>
<string name="version_text" msgid="4001669804596458577">"хувилбар <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клон"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml
index 142a60f..36ee416 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"अनुमती असलेले"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"अनुमती नाही"</string>
<string name="version_text" msgid="4001669804596458577">"आवृत्ती <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोन"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml
index 0d39435..c2f3243 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dibenarkan"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Tidak dibenarkan"</string>
<string name="version_text" msgid="4001669804596458577">"versi <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml
index f87608f..2d08eec 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"ခွင့်ပြုထားသည်"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ခွင့်ပြုမထားပါ"</string>
<string name="version_text" msgid="4001669804596458577">"ဗားရှင်း <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ပုံတူပွား"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml
index 745834d..be739a2 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Tillatt"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ikke tillatt"</string>
<string name="version_text" msgid="4001669804596458577">"versjon <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon av <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
index 4b99bae1..7f3a523 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"अनुमति दिइएका एप"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"अनुमति नदिइएका एप"</string>
<string name="version_text" msgid="4001669804596458577">"संस्करण <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोन"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml
index 9a6b767..4ea7fed 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Toegestaan"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Niet toegestaan"</string>
<string name="version_text" msgid="4001669804596458577">"versie <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Kloon van <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml
index e562d55..501dc5c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"ଅନୁମତି ଦିଆଯାଇଛି"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ଅନୁମତି ଦିଆଯାଇନାହିଁ"</string>
<string name="version_text" msgid="4001669804596458577">"ଭର୍ସନ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> କ୍ଲୋନ"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml
index a24ec90..28fcf0b 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"ਆਗਿਆ ਹੈ"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ਆਗਿਆ ਨਹੀਂ ਹੈ"</string>
<string name="version_text" msgid="4001669804596458577">"ਵਰਜਨ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਦਾ ਕਲੋਨ"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml
index 7da29a5..c947a66 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dozwolone"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Niedozwolone"</string>
<string name="version_text" msgid="4001669804596458577">"wersja <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klonuj: <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml
index 85a42e2..ba92ebb6 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitido"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitido"</string>
<string name="version_text" msgid="4001669804596458577">"Versão <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml
index 9db985d..e7030df 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitida"</string>
<string name="version_text" msgid="4001669804596458577">"versão <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml
index 85a42e2..ba92ebb6 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitido"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitido"</string>
<string name="version_text" msgid="4001669804596458577">"Versão <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml
index e808e4a..c78e9be 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permisă"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nepermisă"</string>
<string name="version_text" msgid="4001669804596458577">"versiunea <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clonă pentru <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml
index 404ed31..3507bc7 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Разрешено"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Запрещено"</string>
<string name="version_text" msgid="4001669804596458577">"версия <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон приложения \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml
index 276cbc4..6014e07 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"ඉඩ දුන්"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ඉඩ නොදෙන"</string>
<string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> අනුවාදය"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ක්ලෝනය"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml
index d8dde40..9888125 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Povolené"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nepovolené"</string>
<string name="version_text" msgid="4001669804596458577">"verzia <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml
index 4cb2a40..74b3ffd 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dovoljeno"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ni dovoljeno"</string>
<string name="version_text" msgid="4001669804596458577">"različica <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klonirani paket <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml
index ba3d3cb..37aa7f0 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Lejohet"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nuk lejohet"</string>
<string name="version_text" msgid="4001669804596458577">"versioni <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon i <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml
index 75caa5a..fe9b323 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Дозвољено"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Није дозвољено"</string>
<string name="version_text" msgid="4001669804596458577">"верзија <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон апликације <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml
index e11bb12..189c26e 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Tillåts"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Tillåts inte"</string>
<string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon av <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml
index be04d8e..241376b 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Inaruhusiwa"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Hairuhusiwi"</string>
<string name="version_text" msgid="4001669804596458577">"toleo la <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Nakala ya <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml
index cab94e2..b8e5d3a 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"அனுமதிக்கப்பட்டது"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"அனுமதிக்கப்படவில்லை"</string>
<string name="version_text" msgid="4001669804596458577">"பதிப்பு <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> குளோன்"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml
index 721e86a..db41032 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"అనుమతించబడినవి"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"అనుమతించబడలేదు"</string>
<string name="version_text" msgid="4001669804596458577">"వెర్షన్ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> క్లోన్"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml
index 0844e24..ac38443 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"อนุญาต"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ไม่อนุญาต"</string>
<string name="version_text" msgid="4001669804596458577">"เวอร์ชัน <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"โคลนของ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml
index 1d0bead..065834b 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Pinapahintulutan"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Hindi pinapahintulutan"</string>
<string name="version_text" msgid="4001669804596458577">"bersyon <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone ng <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml
index e3fcf4d..188a096 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"İzin veriliyor"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"İzin verilmiyor"</string>
<string name="version_text" msgid="4001669804596458577">"sürüm: <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klonu"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml
index 392738c..0a5ca4f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Дозволено"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Заборонено"</string>
<string name="version_text" msgid="4001669804596458577">"версія <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Копія додатка <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml
index c05c387..6b344fc 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"اجازت ہے"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"اجازت نہیں ہے"</string>
<string name="version_text" msgid="4001669804596458577">"ورژن <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کلون"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml
index dbef24b..d5cf2b2 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Được phép"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Không được phép"</string>
<string name="version_text" msgid="4001669804596458577">"phiên bản <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"Bản sao của <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml
index fbd6fc7..fd16dea 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"已允许"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"不允许"</string>
<string name="version_text" msgid="4001669804596458577">"版本 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>克隆"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml
index c05e560..98071b9 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"允許"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"不允許"</string>
<string name="version_text" msgid="4001669804596458577">"版本 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」複製本"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml
index c05e560..03efe37 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"允許"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"不允許"</string>
<string name="version_text" msgid="4001669804596458577">"版本 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」副本"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml
index 45d11cc..088bb0f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml
@@ -23,6 +23,5 @@
<string name="app_permission_summary_allowed" msgid="6115213465364138103">"Kuvumelekile"</string>
<string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Akuvumelekile"</string>
<string name="version_text" msgid="4001669804596458577">"Uhlobo <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
- <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
- <skip />
+ <string name="cloned_app_info_label" msgid="1765651167024478391">"I-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ye-clone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index a188fea..f9745e0 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -309,8 +309,8 @@
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"প্ৰতিটো লগ বাফাৰত ল\'গাৰৰ আকাৰ বাছনি কৰক"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"লগাৰৰ স্থায়ী ষ্ট’ৰেজৰ বস্তুবোৰ মচিবনে?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"পাৰ্ছিছটেণ্ট লগাৰ ব্যৱহাৰ কৰ নিৰীক্ষণ নকৰাৰ সময়ত, আমি আপোনাৰ ডিভাইচত থকা লগাৰ ডেটা নিৱাসীক মচা দৰকাৰ।"</string>
- <string name="select_logpersist_title" msgid="447071974007104196">"ডিভাইচটোত লগাৰৰ ডেটা নিৰবচ্ছিন্নভাৱে সঞ্চয় কৰক"</string>
- <string name="select_logpersist_dialog_title" msgid="7745193591195485594">"ডিভাইচত স্থায়ীভাৱে সঞ্চয় কৰিবলৈ লগ বাফাৰবোৰ বাছনি কৰক"</string>
+ <string name="select_logpersist_title" msgid="447071974007104196">"ডিভাইচটোত লগাৰৰ ডেটা নিৰবচ্ছিন্নভাৱে ষ্ট’ৰ কৰক"</string>
+ <string name="select_logpersist_dialog_title" msgid="7745193591195485594">"ডিভাইচত স্থায়ীভাৱে ষ্ট’ৰ কৰিবলৈ লগ বাফাৰবোৰ বাছনি কৰক"</string>
<string name="select_usb_configuration_title" msgid="6339801314922294586">"ইউএছবি কনফিগাৰেশ্বন বাছনি কৰক"</string>
<string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"ইউএছবি কনফিগাৰেশ্বন বাছনি কৰক"</string>
<string name="allow_mock_location" msgid="2102650981552527884">"নকল অৱস্থানৰ অনুমতি দিয়ক"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 80d90b7..2d01b37 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -173,7 +173,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"Օգտատեր՝ <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Որոշ կանխադրված կարգավորումներ կան"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"Կանխադրված կարգավորումներ չկան"</string>
- <string name="tts_settings" msgid="8130616705989351312">"Տեքստի հնչեցման կարգավորումներ"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"Տեքստի հնչեցման կարգավորումներ"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Տեքստի հնչեցում"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Խոսքի արագությունը"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Տեքստի արտասանման արագությունը"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 23df3be..b2a8459 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -173,7 +173,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"Колдонуучу: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Айрым демейки параметрлер туураланды"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"Демейки маанилер коюлган жок"</string>
- <string name="tts_settings" msgid="8130616705989351312">"Кеп синтезаторунун жөндөөлөрү"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"Кеп синтезаторунун параметрлери"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Кеп синтезатору"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Кеп ылдамдыгы"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Текст айтылчу ылдамдык"</string>
@@ -219,9 +219,9 @@
<string name="development_settings_enable" msgid="4285094651288242183">"Иштеп чыгуучунун параметрлерин иштетүү"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Колдонмо өндүрүү мүмкүнчүлүктөрүн орнотуу"</string>
<string name="development_settings_not_available" msgid="355070198089140951">"Бул колдонуучуга өнүктүүрүүчү мүмкүнчүлүктөрү берилген эмес."</string>
- <string name="vpn_settings_not_available" msgid="2894137119965668920">"Бул колдонуучу VPN жөндөөлөрүн колдоно албайт"</string>
- <string name="tethering_settings_not_available" msgid="266821736434699780">"Бул колдонуучу модем режиминин жөндөөлөрүн өзгөртө албайт"</string>
- <string name="apn_settings_not_available" msgid="1147111671403342300">"Бул колдонуучу мүмкүндүк алуу түйүнүнүн аталышынын жөндөөлөрүн колдоно албайт"</string>
+ <string name="vpn_settings_not_available" msgid="2894137119965668920">"Бул колдонуучу VPN параметрлерин колдоно албайт"</string>
+ <string name="tethering_settings_not_available" msgid="266821736434699780">"Бул колдонуучу модем режиминин параметрлерин өзгөртө албайт"</string>
+ <string name="apn_settings_not_available" msgid="1147111671403342300">"Бул колдонуучу мүмкүндүк алуу түйүнүнүн аталышынын параметрлерин колдоно албайт"</string>
<string name="enable_adb" msgid="8072776357237289039">"USB аркылуу мүчүлүштүктөрдү аныктоо"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"USB компьютерге сайылганда мүчүлүштүктөрдү оңдоо режими иштейт"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"USB аркылуу мүчүлүштүктөрдү аныктоо уруксатын артка кайтаруу"</string>
@@ -322,7 +322,7 @@
<string name="adb_warning_message" msgid="8145270656419669221">"USB-жөндөө - өндүрүү максатында гана түзүлгөн. Аны компүтериңиз менен түзмөгүңүздүн ортосунда берилиштерди алмашуу, түзмөгүңүзгө колдонмолорду эскертүүсүз орнотуу жана лог берилиштерин окуу үчүн колдонсоңуз болот."</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоого уруксат бересизби?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо – өндүрүү максатында гана түзүлгөн. Аны компьютериңиз менен түзмөгүңүздүн ортосунда маалыматты алмашуу, колдонмолорду түзмөгүңүзгө эскертүүсүз орнотуу жана маалыматтар таржымалын окуу үчүн колдонсоңуз болот."</string>
- <string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB жөндөөлөрүнө уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string>
+ <string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB параметрлерине уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Параметрлерди өзгөртүү"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Орнотулуучу колдонмону текшерүү"</string>
@@ -420,7 +420,7 @@
<string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Күйгүзүү үчүн басып коюңуз."</string>
<string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Өчүрүү үчүн басып коюңуз."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Көшүү режиминдеги колдонмонун абалы:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
- <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа файлдарды транскоддоо жөндөөлөрү"</string>
+ <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа файлдарды транскоддоо параметрлери"</string>
<string name="transcode_user_control" msgid="6176368544817731314">"Демейки жүргүзүлгөн транскоддоону өзгөртүп коюу"</string>
<string name="transcode_enable_all" msgid="2411165920039166710">"Транскоддоо жүргүзүүнү иштетүү"</string>
<string name="transcode_default" msgid="3784803084573509491">"Колдонмолордо заманбап форматтар колдоого алынат"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 166b3f0..c148eba 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -173,7 +173,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"အသုံးပြုသူ- <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"မူရင်းအချို့ သတ်မှတ်ပြီး"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"မူရင်း သတ်မှတ်မထားပါ။"</string>
- <string name="tts_settings" msgid="8130616705989351312">"စာ-မှ-စကားပြောင်းခြင်း ဆက်တင်များ"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"စာ-မှ-စကား ဆက်တင်များ"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"စာ-မှ-စကားသို့ အထွက်"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"စကားပြောနှုန်း"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"စာတမ်းအားပြောဆိုသော အမြန်နှုန်း"</string>
diff --git a/packages/SettingsProvider/res/values-ky/strings.xml b/packages/SettingsProvider/res/values-ky/strings.xml
index 8058b4d..7ab6582 100644
--- a/packages/SettingsProvider/res/values-ky/strings.xml
+++ b/packages/SettingsProvider/res/values-ky/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="app_label" msgid="4567566098528588863">"Жөндөөлөрдү сактоо"</string>
- <string name="wifi_softap_config_change" msgid="5688373762357941645">"Байланыш түйүнү жөндөөлөрү өзгөрдү"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Байланыш түйүнү параметрлери өзгөрдү"</string>
<string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Чоо-жайын билүү үчүн басыңыз"</string>
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 7b8ca4d..673a3ab 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -281,6 +281,14 @@
VALIDATORS.put(Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_BRIGHT, BOOLEAN_VALIDATOR);
VALIDATORS.put(
+ Global.Wearable.LOCK_SCREEN_STATE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.LOCK_SCREEN_STATE_NONE),
+ String.valueOf(Global.Wearable.LOCK_SCREEN_STATE_PIN),
+ String.valueOf(Global.Wearable.LOCK_SCREEN_STATE_PATTERN)
+ }));
+ VALIDATORS.put(
Global.Wearable.PAIRED_DEVICE_OS_TYPE,
new DiscreteValueValidator(
new String[] {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index ede69be..75d28d5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -228,9 +228,6 @@
Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
GlobalSettingsProto.App.AUTO_RESTRICTION_ENABLED);
dumpSetting(s, p,
- Settings.Global.FORCED_APP_STANDBY_ENABLED,
- GlobalSettingsProto.App.FORCED_APP_STANDBY_ENABLED);
- dumpSetting(s, p,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
GlobalSettingsProto.App.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED);
p.end(appToken);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f740326..a9c0f00 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1298,30 +1298,40 @@
boolean makeDefault, int operation, int mode) {
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
final String callingPackage = resolveCallingPackage();
-
+ boolean someSettingChanged = false;
// Perform the mutation.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
- return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
+ someSettingChanged = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
callingPackage, false, null,
/* overrideableByRestore */ false);
+ break;
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
+ someSettingChanged = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, false, null);
+ break;
}
case MUTATION_OPERATION_RESET: {
- mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
+ someSettingChanged = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix);
- } return true;
+ break;
+ }
}
}
- return false;
+ if (Settings.IPC_DATA_CACHE_ENABLED) {
+ if (someSettingChanged) {
+ Settings.Config.invalidateValueCache();
+ Settings.Config.invalidateNamespaceCache();
+ }
+ }
+
+ return someSettingChanged;
}
private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) {
@@ -1482,36 +1492,45 @@
}
final String callingPackage = getCallingPackage();
+ boolean someSettingChanged = false;
// Perform the mutation.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
- return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
+ someSettingChanged = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
callingPackage, forceNotify,
CRITICAL_GLOBAL_SETTINGS, overrideableByRestore);
+ break;
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
+ someSettingChanged = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, forceNotify, CRITICAL_GLOBAL_SETTINGS);
+ break;
}
case MUTATION_OPERATION_UPDATE: {
- return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL,
+ someSettingChanged = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
callingPackage, forceNotify, CRITICAL_GLOBAL_SETTINGS);
+ break;
}
case MUTATION_OPERATION_RESET: {
- mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL,
+ someSettingChanged = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, callingPackage, mode, tag);
- } return true;
+ break;
+ }
}
}
- return false;
+ if (Settings.IPC_DATA_CACHE_ENABLED && someSettingChanged) {
+ Settings.Global.invalidateValueCache();
+ }
+
+ return someSettingChanged;
}
private PackageInfo getCallingPackageInfo(int userId) {
@@ -1954,31 +1973,39 @@
cacheFile.delete();
}
+ boolean someSettingChanged = false;
// Mutate the value.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
+ someSettingChanged = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null, overrideableByRestore);
+ break;
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
+ someSettingChanged = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, false, null);
+ break;
}
case MUTATION_OPERATION_UPDATE: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
+ someSettingChanged = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null);
+ break;
}
+ default:
+ Slog.e(LOG_TAG, "Unknown operation code: " + operation);
}
- Slog.e(LOG_TAG, "Unknown operation code: " + operation);
- return false;
}
+ if (Settings.IPC_DATA_CACHE_ENABLED && someSettingChanged) {
+ Settings.System.invalidateValueCache();
+ }
+ return someSettingChanged;
}
private boolean hasWriteSecureSettingsPermission() {
@@ -2969,6 +2996,9 @@
final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
final SettingsState systemSettingsState = mSettingsStates.get(systemKey);
if (systemSettingsState != null) {
+ if (Settings.IPC_DATA_CACHE_ENABLED) {
+ Settings.System.invalidateValueCache();
+ }
if (permanently) {
mSettingsStates.remove(systemKey);
systemSettingsState.destroyLocked(null);
@@ -2986,6 +3016,9 @@
final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
final SettingsState secureSettingsState = mSettingsStates.get(secureKey);
if (secureSettingsState != null) {
+ if (Settings.IPC_DATA_CACHE_ENABLED) {
+ Settings.Secure.invalidateValueCache();
+ }
if (permanently) {
mSettingsStates.remove(secureKey);
secureSettingsState.destroyLocked(null);
@@ -3140,25 +3173,25 @@
return settingsState.getSettingLocked(name);
}
- public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+ public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
- resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
+ return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
null);
}
- public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+ public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag, @Nullable String prefix) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState == null) {
- return;
+ return false;
}
banConfigurationIfNecessary(type, prefix, settingsState);
+ boolean someSettingChanged = false;
switch (mode) {
case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
- boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (packageName.equals(setting.getPackageName())) {
if ((tag != null && !tag.equals(setting.getTag()))
@@ -3179,7 +3212,6 @@
case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
- boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
@@ -3200,7 +3232,6 @@
case Settings.RESET_MODE_UNTRUSTED_CHANGES: {
for (String name : settingsState.getSettingNamesLocked()) {
- boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
@@ -3228,7 +3259,6 @@
case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
Setting setting = settingsState.getSettingLocked(name);
- boolean someSettingChanged = false;
if (prefix != null && !setting.getName().startsWith(prefix)) {
continue;
}
@@ -3249,6 +3279,7 @@
}
} break;
}
+ return someSettingChanged;
}
public void removeSettingsForPackageLocked(String packageName, int userId) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index db7032e..3782ce4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -285,7 +285,6 @@
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
- Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.WIFI_ON_WHEN_PROXY_DISCONNECTED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
@@ -660,7 +659,8 @@
Settings.Global.Wearable.BEDTIME_MODE,
Settings.Global.Wearable.BEDTIME_HARD_MODE,
Settings.Global.Wearable.EARLY_UPDATES_STATUS,
- Settings.Global.Wearable.RSB_WAKE_ENABLED);
+ Settings.Global.Wearable.RSB_WAKE_ENABLED,
+ Settings.Global.Wearable.LOCK_SCREEN_STATE);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b4598bf..2779fa2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -57,6 +57,7 @@
<uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
+ <uses-permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
@@ -369,6 +370,9 @@
<!-- Permission needed to test wallpapers supporting ambient mode -->
<uses-permission android:name="android.permission.AMBIENT_WALLPAPER" />
+ <!-- Permission needed to test wallpaper read methods -->
+ <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />
+
<!-- Permission required to test ContentResolver caching. -->
<uses-permission android:name="android.permission.CACHE_CONTENT" />
@@ -508,6 +512,9 @@
<!-- Permission needed for CTS test - DefaultDisplayModeTest -->
<uses-permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE" />
+ <!-- Permission needed for CTS test - HdrConversionTest -->
+ <uses-permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE" />
+
<!-- Permissions needed for manual testing telephony time zone detector behavior -->
<uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
@@ -796,12 +803,19 @@
<!-- Permission required for CTS test - CtsPackageInstallTestCases-->
<uses-permission android:name="android.permission.GET_APP_METADATA" />
+ <!-- Permission required for CTS test - CtsHealthConnectDeviceTestCases -->
+ <uses-permission android:name="android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA" />
+ <uses-permission android:name="android.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA" />
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED"/>
<!-- Permissions required for CTS test - CtsBroadcastRadioTestCases -->
<uses-permission android:name="android.permission.ACCESS_BROADCAST_RADIO" />
+ <!-- Permission required for CTS test - ActivityCaptureCallbackTests -->
+ <uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/res/values-mk/strings.xml b/packages/Shell/res/values-mk/strings.xml
index 0856198..93d4d52 100644
--- a/packages/Shell/res/values-mk/strings.xml
+++ b/packages/Shell/res/values-mk/strings.xml
@@ -37,7 +37,7 @@
<string name="bugreport_info_action" msgid="2158204228510576227">"Детали"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Слика од екранот"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Успешно е направена слика од екранот."</string>
- <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Не може да се направи слика од екранот."</string>
+ <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Не може да се зачува слика од екранот."</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Детали за извештајот за грешки <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"Име на датотека"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"Наслов на грешката"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
new file mode 100644
index 0000000..a64a0d1
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,9c1.65,0 3,1.35 3,3s-1.35,3 -3,3 -3,-1.35 -3,-3 1.35,-3 3,-3m0,-2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
new file mode 100644
index 0000000..40423c7
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
new file mode 100644
index 0000000..a0f7b5d
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
new file mode 100644
index 0000000..8757f22
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v6L4,17v2h16v-2h-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6zM12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
new file mode 100644
index 0000000..049013a
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
new file mode 100644
index 0000000..4f25e7d
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46 0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19c-0.59,-0.45 -0.74,-1.26 -0.37,-1.88l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91c-0.1,0.68 -0.72,1.22 -1.46,1.22zM10.62,20.25h2.76l0.37,-2.55 0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34 2.38,0.96 1.38,-2.4 -2.03,-1.58 0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78s-0.03,-0.53 -0.06,-0.78l-0.07,-0.56 2.03,-1.58 -1.39,-2.4 -2.39,0.96 -0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77l-0.52,-0.22 -0.37,-2.55h-2.76l-0.37,2.55 -0.53,0.21c-0.44,0.19 -0.88,0.44 -1.34,0.79l-0.45,0.33 -2.38,-0.95 -1.39,2.39 2.03,1.58 -0.07,0.56c-0.03,0.26 -0.06,0.53 -0.06,0.79s0.02,0.53 0.06,0.78l0.07,0.56 -2.03,1.58 1.38,2.4 2.39,-0.96 0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22 0.38,2.55z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
new file mode 100644
index 0000000..38234c0
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M2,7h4v10H2V7zM7,19h10V5H7V19zM9,7h6v10H9V7zM18,7h4v10h-4V7z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
new file mode 100644
index 0000000..6d7f49c
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,21L7,21v-1h10v1zM17,18L7,18L7,6h10v12zM17,4L7,4L7,3h10v1zM9.5,8.5L12,8.5L12,7L8,7v4h1.5zM12,17h4v-4h-1.5v2.5L12,15.5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
new file mode 100644
index 0000000..5ed6f19
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.5,4c-2.61,0.7 -5.67,1 -8.5,1s-5.89,-0.3 -8.5,-1L3,6c1.86,0.5 4,0.83 6,1v13h2v-6h2v6h2L15,7c2,-0.17 4.14,-0.5 6,-1l-0.5,-2zM12,4c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM7,24h2v-2L7,22v2zM11,24h2v-2h-2v2zM15,24h2v-2h-2v2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
new file mode 100644
index 0000000..16653e8
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M16,7.97v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02 0,-1.77 -1.02,-3.29 -2.5,-4.03zM5,9v6h4l5,5L14,4L9,9L5,9zM12,8.83v6.34L9.83,13L7,13v-2h2.83L12,8.83z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
new file mode 100644
index 0000000..e572c6a
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM10,8.83v6.34L7.83,13L5,13v-2h2.83L10,8.83zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77 0,-4.28 -2.99,-7.86 -7,-8.77z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 658c03b..91cb4ba 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -50,6 +50,17 @@
</LinearLayout>
+ <TextView
+ android:id="@+id/snackbar"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:gravity="center_vertical"
+ android:textColor="@color/colorControlNormal"
+ android:textSize="@dimen/label_text_size"
+ android:background="@color/snackbar_bg_color"/>
+
<View
android:id="@+id/bottom_listDivider"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
index 33c0cca..a600ec6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
@@ -19,5 +19,6 @@
<color name="footer_icon_enabled_color">#E8EAED</color>
<color name="footer_icon_disabled_color">#5F6368</color>
<color name="colorControlNormal">#202124</color>
+ <color name="snackbar_bg_color">@android:color/white</color>
</resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
index 36d1fc1..c1494f9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
@@ -20,6 +20,7 @@
<color name="footer_icon_enabled_color">@android:color/black</color>
<color name="footer_icon_disabled_color">#ddd</color>
<color name="colorControlNormal">@android:color/white</color>
+ <color name="snackbar_bg_color">#313235</color>
<color name="colorAccessibilityMenuIcon">#3AA757</color>
</resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
index fa42e61..b6328f0 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
@@ -15,9 +15,16 @@
*/
package com.android.systemui.accessibility.accessibilitymenu.model;
+import android.util.Log;
+
import com.android.systemui.accessibility.accessibilitymenu.R;
-/** Provides a data structure for a11y menu shortcuts. */
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides a data structure for a11y menu shortcuts.
+ */
public class A11yMenuShortcut {
public enum ShortcutId {
@@ -38,6 +45,88 @@
private static final String TAG = "A11yMenuShortcut";
+ // Index of resource ID in the array, shortcutResource.
+ private static final int IMG_SRC_INDEX = 0;
+ private static final int IMG_COLOR_INDEX = 1;
+ private static final int CONTENT_DESCRIPTION_INDEX = 2;
+ private static final int LABEL_TEXT_INDEX = 3;
+
+ /** Map stores all shortcut resource IDs that is in matching order of defined shortcut. */
+ private static final Map<ShortcutId, int[]> sShortcutResource = new HashMap<>() {{
+ put(ShortcutId.ID_ASSISTANT_VALUE, new int[] {
+ R.drawable.ic_logo_assistant_32dp,
+ R.color.assistant_color,
+ R.string.assistant_utterance,
+ R.string.assistant_label,
+ });
+ put(ShortcutId.ID_A11YSETTING_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_settings_24dp,
+ R.color.a11y_settings_color,
+ R.string.a11y_settings_label,
+ R.string.a11y_settings_label,
+ });
+ put(ShortcutId.ID_POWER_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_power_24dp,
+ R.color.power_color,
+ R.string.power_utterance,
+ R.string.power_label,
+ });
+ put(ShortcutId.ID_RECENT_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_recent_apps_24dp,
+ R.color.recent_apps_color,
+ R.string.recent_apps_label,
+ R.string.recent_apps_label,
+ });
+ put(ShortcutId.ID_LOCKSCREEN_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_lock_24dp,
+ R.color.lockscreen_color,
+ R.string.lockscreen_label,
+ R.string.lockscreen_label,
+ });
+ put(ShortcutId.ID_QUICKSETTING_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_quick_settings_24dp,
+ R.color.quick_settings_color,
+ R.string.quick_settings_label,
+ R.string.quick_settings_label,
+ });
+ put(ShortcutId.ID_NOTIFICATION_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_notifications_24dp,
+ R.color.notifications_color,
+ R.string.notifications_label,
+ R.string.notifications_label,
+ });
+ put(ShortcutId.ID_SCREENSHOT_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_screenshot_24dp,
+ R.color.screenshot_color,
+ R.string.screenshot_utterance,
+ R.string.screenshot_label,
+ });
+ put(ShortcutId.ID_BRIGHTNESS_UP_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_brightness_up_24dp,
+ R.color.brightness_color,
+ R.string.brightness_up_label,
+ R.string.brightness_up_label,
+ });
+ put(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_brightness_down_24dp,
+ R.color.brightness_color,
+ R.string.brightness_down_label,
+ R.string.brightness_down_label,
+ });
+ put(ShortcutId.ID_VOLUME_UP_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_volume_up_24dp,
+ R.color.volume_color,
+ R.string.volume_up_label,
+ R.string.volume_up_label,
+ });
+ put(ShortcutId.ID_VOLUME_DOWN_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_volume_down_24dp,
+ R.color.volume_color,
+ R.string.volume_down_label,
+ R.string.volume_down_label,
+ });
+ }};
+
/** Shortcut id used to identify. */
private int mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal();
@@ -53,17 +142,33 @@
/**
* Sets Id to shortcut, checks the value first and updates shortcut resources. It will set id to
+ * default value {@link ShortcutId.UNSPECIFIED_ID_VALUE} if invalid.
*
* @param id id set to shortcut
*/
public void setId(int id) {
mShortcutId = id;
- // TODO(jonesriley) load the proper resources based on id
- imageSrc = R.drawable.ic_logo_assistant_32dp;
- imageColor = android.R.color.darker_gray;
- imgContentDescription = R.string.empty_content;
- labelText = R.string.empty_content;
+ if (id < ShortcutId.UNSPECIFIED_ID_VALUE.ordinal()
+ || id > ShortcutId.values().length) {
+ mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal();
+ Log.w(
+ TAG, String.format(
+ "setId to default UNSPECIFIED_ID as id is invalid. "
+ + "Max value is %d while id is %d",
+ ShortcutId.values().length, id
+ ));
+ }
+ int[] resources = sShortcutResource.getOrDefault(ShortcutId.values()[id], new int[] {
+ R.drawable.ic_add_32dp,
+ android.R.color.darker_gray,
+ R.string.empty_content,
+ R.string.empty_content,
+ });
+ imageSrc = resources[IMG_SRC_INDEX];
+ imageColor = resources[IMG_COLOR_INDEX];
+ imgContentDescription = resources[CONTENT_DESCRIPTION_INDEX];
+ labelText = resources[LABEL_TEXT_INDEX];
}
public int getId() {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 740bc8a..7bdb8f9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -18,10 +18,14 @@
import static java.lang.Math.max;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -31,8 +35,11 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
-import android.widget.Toast;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.R;
@@ -69,11 +76,15 @@
private ViewGroup mLayout;
private WindowManager.LayoutParams mLayoutParameter;
private A11yMenuViewPager mA11yMenuViewPager;
+ private Handler mHandler;
+ private AccessibilityManager mAccessibilityManager;
public A11yMenuOverlayLayout(AccessibilityMenuService service) {
mService = service;
mWindowManager = mService.getSystemService(WindowManager.class);
configureLayout();
+ mHandler = new Handler(Looper.getMainLooper());
+ mAccessibilityManager = mService.getSystemService(AccessibilityManager.class);
}
/** Creates Accessibility menu layout and configure layout parameters. */
@@ -261,9 +272,30 @@
mLayout.setVisibility((mLayout.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE);
}
- /** Shows hint text on Toast. */
- public void showToast(String text) {
- final View viewPos = mLayout.findViewById(R.id.coordinatorLayout);
- Toast.makeText(viewPos.getContext(), text, Toast.LENGTH_SHORT).show();
+ /** Shows hint text on a minimal Snackbar-like text view. */
+ public void showSnackbar(String text) {
+ final int animationDurationMs = 300;
+ final int timeoutDurationMs = mAccessibilityManager.getRecommendedTimeoutMillis(2000,
+ AccessibilityManager.FLAG_CONTENT_TEXT);
+
+ final TextView snackbar = mLayout.findViewById(R.id.snackbar);
+ snackbar.setText(text);
+
+ // Remove any existing fade-out animation before starting any new animations.
+ mHandler.removeCallbacksAndMessages(null);
+
+ if (snackbar.getVisibility() != View.VISIBLE) {
+ snackbar.setAlpha(0f);
+ snackbar.setVisibility(View.VISIBLE);
+ snackbar.animate().alpha(1f).setDuration(animationDurationMs).setListener(null);
+ }
+ mHandler.postDelayed(() -> snackbar.animate().alpha(0f).setDuration(
+ animationDurationMs).setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ snackbar.setVisibility(View.GONE);
+ }
+ }), timeoutDurationMs);
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
new file mode 100644
index 0000000..1c9dabb
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.animation
+
+interface AnimationFeatureFlags {
+ val isPredictiveBackQsDialogAnim: Boolean
+ get() = false
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index a3ed085..e91a671 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -33,8 +33,13 @@
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
import android.widget.FrameLayout
+import android.window.OnBackInvokedDispatcher
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CujType
+import com.android.systemui.animation.back.BackAnimationSpec
+import com.android.systemui.animation.back.applyTo
+import com.android.systemui.animation.back.floatingSystemSurfacesForSysUi
+import com.android.systemui.animation.back.onBackAnimationCallbackFrom
import kotlin.math.roundToInt
private const val TAG = "DialogLaunchAnimator"
@@ -55,8 +60,9 @@
constructor(
private val callback: Callback,
private val interactionJankMonitor: InteractionJankMonitor,
+ private val featureFlags: AnimationFeatureFlags,
private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
- private val isForTesting: Boolean = false
+ private val isForTesting: Boolean = false,
) {
private companion object {
private val TIMINGS = ActivityLaunchAnimator.TIMINGS
@@ -273,15 +279,16 @@
val animatedDialog =
AnimatedDialog(
- launchAnimator,
- callback,
- interactionJankMonitor,
- controller,
+ launchAnimator = launchAnimator,
+ callback = callback,
+ interactionJankMonitor = interactionJankMonitor,
+ controller = controller,
onDialogDismissed = { openedDialogs.remove(it) },
dialog = dialog,
- animateBackgroundBoundsChange,
- animatedParent,
- isForTesting,
+ animateBackgroundBoundsChange = animateBackgroundBoundsChange,
+ parentAnimatedDialog = animatedParent,
+ forceDisableSynchronization = isForTesting,
+ featureFlags = featureFlags,
)
openedDialogs.add(animatedDialog)
@@ -517,6 +524,7 @@
* Whether synchronization should be disabled, which can be useful if we are running in a test.
*/
private val forceDisableSynchronization: Boolean,
+ private val featureFlags: AnimationFeatureFlags,
) {
/**
* The DecorView of this dialog window.
@@ -778,12 +786,45 @@
// the dialog.
dialog.setDismissOverride(this::onDialogDismissed)
+ if (featureFlags.isPredictiveBackQsDialogAnim) {
+ // TODO(b/265923095) Improve animations for QS dialogs on configuration change
+ registerOnBackInvokedCallback(targetView = dialogContentWithBackground)
+ }
+
// Show the dialog.
dialog.show()
-
moveSourceDrawingToDialog()
}
+ private fun registerOnBackInvokedCallback(targetView: View) {
+ val metrics = targetView.resources.displayMetrics
+
+ val onBackAnimationCallback =
+ onBackAnimationCallbackFrom(
+ backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(metrics),
+ displayMetrics = metrics, // TODO(b/265060720): We could remove this
+ onBackProgressed = { backTransformation -> backTransformation.applyTo(targetView) },
+ onBackInvoked = { dialog.dismiss() },
+ )
+
+ val dispatcher = dialog.onBackInvokedDispatcher
+ targetView.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ dispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ onBackAnimationCallback
+ )
+ }
+
+ override fun onViewDetachedFromWindow(v: View) {
+ targetView.removeOnAttachStateChangeListener(this)
+ dispatcher.unregisterOnBackInvokedCallback(onBackAnimationCallback)
+ }
+ }
+ )
+ }
+
private fun moveSourceDrawingToDialog() {
if (decorView.viewRootImpl == null) {
// Make sure that we have access to the dialog view root to move the drawing to the
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
new file mode 100644
index 0000000..f3d8b17
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation.back
+
+import android.util.DisplayMetrics
+import android.view.animation.Interpolator
+import android.window.BackEvent
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.util.dpToPx
+
+/** Used to convert [BackEvent] into a [BackTransformation]. */
+fun interface BackAnimationSpec {
+
+ /** Computes transformation based on a [backEvent] and sets it to [result]. */
+ fun getBackTransformation(
+ backEvent: BackEvent,
+ progressY: Float, // TODO(b/265060720): Remove progressY. Could be retrieved from backEvent
+ result: BackTransformation,
+ )
+
+ companion object
+}
+
+/** Create a [BackAnimationSpec] from [displayMetrics] and design specs. */
+fun BackAnimationSpec.Companion.createFloatingSurfaceAnimationSpec(
+ displayMetrics: DisplayMetrics,
+ maxMarginXdp: Float,
+ maxMarginYdp: Float,
+ minScale: Float,
+ translateXEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+ translateYEasing: Interpolator = Interpolators.LINEAR,
+ scaleEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+): BackAnimationSpec {
+ val screenWidthPx = displayMetrics.widthPixels
+ val screenHeightPx = displayMetrics.heightPixels
+
+ val maxMarginXPx = maxMarginXdp.dpToPx(displayMetrics)
+ val maxMarginYPx = maxMarginYdp.dpToPx(displayMetrics)
+ val maxTranslationXByScale = (screenWidthPx - screenWidthPx * minScale) / 2
+ val maxTranslationX = maxTranslationXByScale - maxMarginXPx
+ val maxTranslationYByScale = (screenHeightPx - screenHeightPx * minScale) / 2
+ val maxTranslationY = maxTranslationYByScale - maxMarginYPx
+ val minScaleReversed = 1f - minScale
+
+ return BackAnimationSpec { backEvent, progressY, result ->
+ val direction = if (backEvent.swipeEdge == BackEvent.EDGE_LEFT) 1 else -1
+ val progressX = backEvent.progress
+
+ val ratioTranslateX = translateXEasing.getInterpolation(progressX)
+ val ratioTranslateY = translateYEasing.getInterpolation(progressY)
+ val ratioScale = scaleEasing.getInterpolation(progressX)
+
+ result.apply {
+ translateX = ratioTranslateX * direction * maxTranslationX
+ translateY = ratioTranslateY * maxTranslationY
+ scale = 1f - (ratioScale * minScaleReversed)
+ }
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt
new file mode 100644
index 0000000..c6b7073
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation.back
+
+import android.util.DisplayMetrics
+
+/**
+ * SysUI transitions - Dismiss app (ST1) Return to launching surface or place of origin
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-1-dismiss-app
+ */
+fun BackAnimationSpec.Companion.dismissAppForSysUi(
+ displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+ BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+ displayMetrics = displayMetrics,
+ maxMarginXdp = 8f,
+ maxMarginYdp = 8f,
+ minScale = 0.8f,
+ )
+
+/**
+ * SysUI transitions - Cross task (ST2) Return to previous task/app, keeping the current one open
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-2-cross-task
+ */
+fun BackAnimationSpec.Companion.crossTaskForSysUi(
+ displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+ BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+ displayMetrics = displayMetrics,
+ maxMarginXdp = 8f,
+ maxMarginYdp = 8f,
+ minScale = 0.8f,
+ )
+
+/**
+ * SysUI transitions - Inner area dismiss (ST3) Dismiss non-detachable surface
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-3-inner-area-dismiss
+ */
+fun BackAnimationSpec.Companion.innerAreaDismissForSysUi(
+ displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+ BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+ displayMetrics = displayMetrics,
+ maxMarginXdp = 0f,
+ maxMarginYdp = 0f,
+ minScale = 0.9f,
+ )
+
+/**
+ * SysUI transitions - Floating system surfaces (ST4)
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-4-floating-system-surfaces
+ */
+fun BackAnimationSpec.Companion.floatingSystemSurfacesForSysUi(
+ displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+ BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+ displayMetrics = displayMetrics,
+ maxMarginXdp = 8f,
+ maxMarginYdp = 8f,
+ minScale = 0.8f,
+ )
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt
new file mode 100644
index 0000000..49d1fb4
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation.back
+
+import android.view.View
+
+/**
+ * This object that represents the transformation to apply to the target. The properties of this
+ * object are mutable for performance reasons (avoid recreating this object)
+ */
+data class BackTransformation(
+ var translateX: Float = Float.NaN,
+ var translateY: Float = Float.NaN,
+ var scale: Float = Float.NaN,
+)
+
+/** Apply the transformation to the [targetView] */
+fun BackTransformation.applyTo(targetView: View) {
+ if (translateX.isFinite()) targetView.translationX = translateX
+ if (translateY.isFinite()) targetView.translationY = translateY
+ if (scale.isFinite()) {
+ targetView.scaleX = scale
+ targetView.scaleY = scale
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
new file mode 100644
index 0000000..33d14b1
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation.back
+
+import android.util.DisplayMetrics
+import android.window.BackEvent
+import android.window.OnBackAnimationCallback
+
+/**
+ * Generates an [OnBackAnimationCallback] given a [backAnimationSpec]. [onBackProgressed] will be
+ * called on each update passing the current [BackTransformation].
+ *
+ * Optionally, you can specify [onBackStarted], [onBackInvoked], and [onBackCancelled] callbacks.
+ */
+fun onBackAnimationCallbackFrom(
+ backAnimationSpec: BackAnimationSpec,
+ displayMetrics: DisplayMetrics, // TODO(b/265060720): We could remove this
+ onBackProgressed: (BackTransformation) -> Unit,
+ onBackStarted: (BackEvent) -> Unit = {},
+ onBackInvoked: () -> Unit = {},
+ onBackCancelled: () -> Unit = {},
+): OnBackAnimationCallback {
+ return object : OnBackAnimationCallback {
+ private var initialY = 0f
+ private val lastTransformation = BackTransformation()
+
+ override fun onBackStarted(backEvent: BackEvent) {
+ initialY = backEvent.touchY
+ onBackStarted(backEvent)
+ }
+
+ override fun onBackProgressed(backEvent: BackEvent) {
+ val progressY = (backEvent.touchY - initialY) / displayMetrics.heightPixels
+
+ backAnimationSpec.getBackTransformation(
+ backEvent = backEvent,
+ progressY = progressY,
+ result = lastTransformation,
+ )
+
+ onBackProgressed(lastTransformation)
+ }
+
+ override fun onBackInvoked() {
+ onBackInvoked()
+ }
+
+ override fun onBackCancelled() {
+ onBackCancelled()
+ }
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt b/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt
new file mode 100644
index 0000000..4bc9972
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+import android.content.Context
+import android.content.res.Resources
+import android.util.DisplayMetrics
+import android.util.TypedValue
+
+/** Convert [this] number of dps to device pixels. */
+fun Number.dpToPx(context: Context): Float = dpToPx(resources = context.resources)
+
+/** Convert [this] number of dps to device pixels. */
+fun Number.dpToPx(resources: Resources): Float = dpToPx(displayMetrics = resources.displayMetrics)
+
+/** Convert [this] number of dps to device pixels. */
+fun Number.dpToPx(displayMetrics: DisplayMetrics): Float =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, toFloat(), displayMetrics)
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 4e96dda..cfc38df 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -33,8 +33,8 @@
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
-import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
import androidx.compose.material3.contentColorFor
+import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
@@ -65,21 +65,17 @@
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.findRootCoordinates
-import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewTreeLifecycleOwner
import androidx.lifecycle.ViewTreeViewModelStoreOwner
-import com.android.compose.runtime.movableContentOf
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.LaunchAnimator
import kotlin.math.max
import kotlin.math.min
-import kotlin.math.roundToInt
/**
* Create an expandable shape that can launch into an Activity or a Dialog.
@@ -220,21 +216,8 @@
// If this expandable is expanded when it's being directly clicked on, let's ensure that it has
// the minimum interactive size followed by all M3 components (48.dp).
val minInteractiveSizeModifier =
- if (onClick != null && LocalMinimumTouchTargetEnforcement.current) {
- // TODO(b/242040009): Replace this by Modifier.minimumInteractiveComponentSize() once
- // http://aosp/2305511 is available.
- val minTouchSize = LocalViewConfiguration.current.minimumTouchTargetSize
- Modifier.layout { measurable, constraints ->
- // Copied from androidx.compose.material3.InteractiveComponentSize.kt
- val placeable = measurable.measure(constraints)
- val width = maxOf(placeable.width, minTouchSize.width.roundToPx())
- val height = maxOf(placeable.height, minTouchSize.height.roundToPx())
- layout(width, height) {
- val centerX = ((width - placeable.width) / 2f).roundToInt()
- val centerY = ((height - placeable.height) / 2f).roundToInt()
- placeable.place(centerX, centerY)
- }
- }
+ if (onClick != null) {
+ Modifier.minimumInteractiveComponentSize()
} else {
Modifier
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
index 18e8a96..bf922bc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
@@ -21,4 +21,5 @@
const val MESSAGE_ID_SLOT_SELECTED = 1337
const val KEY_SLOT_ID = "slot_id"
const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
+ const val KEY_HIGHLIGHT_QUICK_AFFORDANCES = "highlight_quick_affordances"
}
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index ccb35fa..79d5718 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -52,6 +52,10 @@
* Unselect an already-selected quick affordance from a slot
* Unselect all already-selected quick affordances from a slot
+## Testing
+* Add a unit test for your implementation of `KeyguardQuickAffordanceConfig`
+* Manually verify that your implementation works in multi-user environments from both the main user and a secondary user
+
## Debugging
To see the current state of the system, you can run `dumpsys`:
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 3d3ccf4..a38afdf 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -197,13 +197,10 @@
-packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -611,7 +608,6 @@
-packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index bc6e5ec..e0cc8f4 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -50,16 +50,24 @@
String TAG = "BcSmartspaceDataPlugin";
/** Register a listener to get Smartspace data. */
- void registerListener(SmartspaceTargetListener listener);
+ default void registerListener(SmartspaceTargetListener listener) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/** Unregister a listener. */
- void unregisterListener(SmartspaceTargetListener listener);
+ default void unregisterListener(SmartspaceTargetListener listener) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/** Register a SmartspaceEventNotifier. */
- default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) {}
+ default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/** Push a SmartspaceTargetEvent to the SmartspaceEventNotifier. */
- default void notifySmartspaceEvent(SmartspaceTargetEvent event) {}
+ default void notifySmartspaceEvent(SmartspaceTargetEvent event) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/** Allows for notifying the SmartspaceSession of SmartspaceTargetEvents. */
interface SmartspaceEventNotifier {
@@ -72,16 +80,20 @@
* will be responsible for correctly setting the LayoutParams
*/
default SmartspaceView getView(ViewGroup parent) {
- return null;
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
}
/**
* As the smartspace view becomes available, allow listeners to receive an event.
*/
- default void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) { }
+ default void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/** Updates Smartspace data and propagates it to any listeners. */
- void onTargetsAvailable(List<SmartspaceTarget> targets);
+ default void onTargetsAvailable(List<SmartspaceTarget> targets) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/** Provides Smartspace data to registered listeners. */
interface SmartspaceTargetListener {
@@ -96,7 +108,9 @@
/**
* Sets {@link BcSmartspaceConfigPlugin}.
*/
- void registerConfigProvider(BcSmartspaceConfigPlugin configProvider);
+ default void registerConfigProvider(BcSmartspaceConfigPlugin configProvider) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/**
* Primary color for unprotected text
@@ -138,28 +152,38 @@
/**
* Set or clear Do Not Disturb information.
*/
- void setDnd(@Nullable Drawable image, @Nullable String description);
+ default void setDnd(@Nullable Drawable image, @Nullable String description) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/**
* Set or clear next alarm information
*/
- void setNextAlarm(@Nullable Drawable image, @Nullable String description);
+ default void setNextAlarm(@Nullable Drawable image, @Nullable String description) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/**
* Set or clear device media playing
*/
- void setMediaTarget(@Nullable SmartspaceTarget target);
+ default void setMediaTarget(@Nullable SmartspaceTarget target) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/**
* Get the index of the currently selected page.
*/
- int getSelectedPage();
+ default int getSelectedPage() {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
/**
* Return the top padding value from the currently visible card, or 0 if there is no current
* card.
*/
- int getCurrentCardTopPadding();
+ default int getCurrentCardTopPadding() {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
}
/** Interface for launching Intents, which can differ on the lockscreen */
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index 9ed3bac..70b5d73 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -105,6 +105,11 @@
default void onDozingChanged(boolean isDozing) {}
/**
+ * Callback to be notified when Dreaming changes. Dreaming is stored separately from state.
+ */
+ default void onDreamingChanged(boolean isDreaming) {}
+
+ /**
* Callback to be notified when the doze amount changes. Useful for animations.
* Note: this will be called for each animation frame. Please be careful to avoid
* performance regressions.
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
index 2cac9c7..90851e2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -45,7 +45,7 @@
android:id="@+id/user_switcher_header"
android:textDirection="locale"
android:layout_width="@dimen/bouncer_user_switcher_width"
- android:layout_height="wrap_content" />
+ android:layout_height="match_parent" />
</com.android.keyguard.KeyguardUserSwitcherAnchor>
</LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index a1068c6..6c8db91 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -25,9 +25,6 @@
<!-- Margin around the various security views -->
<dimen name="keyguard_security_view_top_margin">12dp</dimen>
- <!-- Padding for the lock icon on the keyguard -->
- <dimen name="lock_icon_padding">16dp</dimen>
-
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">100dp</dimen>
<dimen name="widget_label_font_size">18sp</dimen>
diff --git a/packages/SystemUI/res-product/values-pt-rBR/strings.xml b/packages/SystemUI/res-product/values-pt-rBR/strings.xml
index 004a499..bccf53d 100644
--- a/packages/SystemUI/res-product/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-product/values-pt-rBR/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desbloqueie seu smartphone para ver mais opções"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Desbloqueie seu tablet para ver mais opções"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Desbloqueie seu dispositivo para ver mais opções"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Mídia tocando neste smartphone"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Tocando neste smartphone"</string>
<string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Mídia tocando neste tablet"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-pt/strings.xml b/packages/SystemUI/res-product/values-pt/strings.xml
index 004a499..bccf53d 100644
--- a/packages/SystemUI/res-product/values-pt/strings.xml
+++ b/packages/SystemUI/res-product/values-pt/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desbloqueie seu smartphone para ver mais opções"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Desbloqueie seu tablet para ver mais opções"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Desbloqueie seu dispositivo para ver mais opções"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Mídia tocando neste smartphone"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Tocando neste smartphone"</string>
<string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Mídia tocando neste tablet"</string>
</resources>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
index acd2462..7d03b0d 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
@@ -21,7 +21,7 @@
<item android:state_selected="true">
<shape android:shape="oval">
<stroke
- android:color="@color/control_primary_text"
+ android:color="?android:attr/textColorPrimary"
android:width="2dp"/>
<size
android:width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
new file mode 100644
index 0000000..3807b92
--- /dev/null
+++ b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+<ripple
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="28dp" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="28dp" />
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml b/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml
deleted file mode 100644
index 1a1fc75..0000000
--- a/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml
+++ /dev/null
@@ -1,160 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<com.android.systemui.screenshot.DraggableConstraintLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/clipboard_ui"
- android:theme="@style/FloatingOverlay"
- android:alpha="0"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/clipboard_overlay_window_name">
- <ImageView
- android:id="@+id/actions_container_background"
- android:visibility="gone"
- android:layout_height="0dp"
- android:layout_width="0dp"
- android:elevation="4dp"
- android:background="@drawable/action_chip_container_background"
- android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
- app:layout_constraintBottom_toBottomOf="@+id/actions_container"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/actions_container"
- app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
- <HorizontalScrollView
- android:id="@+id/actions_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
- android:paddingEnd="@dimen/overlay_action_container_padding_right"
- android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
- android:elevation="4dp"
- android:scrollbars="none"
- android:layout_marginBottom="4dp"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintWidth_percent="1.0"
- app:layout_constraintWidth_max="wrap"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@+id/preview_border"
- app:layout_constraintEnd_toEndOf="parent">
- <LinearLayout
- android:id="@+id/actions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:animateLayoutChanges="true">
- <include layout="@layout/overlay_action_chip"
- android:id="@+id/share_chip"/>
- <include layout="@layout/overlay_action_chip"
- android:id="@+id/remote_copy_chip"/>
- <include layout="@layout/overlay_action_chip"
- android:id="@+id/edit_chip"/>
- </LinearLayout>
- </HorizontalScrollView>
- <View
- android:id="@+id/preview_border"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="@dimen/overlay_offset_x"
- android:layout_marginBottom="12dp"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- android:elevation="7dp"
- app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
- app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
- android:background="@drawable/overlay_border"/>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/clipboard_preview_end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierMargin="@dimen/overlay_border_width"
- app:barrierDirection="end"
- app:constraint_referenced_ids="clipboard_preview"/>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/clipboard_preview_top"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierDirection="top"
- app:barrierMargin="@dimen/overlay_border_width_neg"
- app:constraint_referenced_ids="clipboard_preview"/>
- <FrameLayout
- android:id="@+id/clipboard_preview"
- android:elevation="7dp"
- android:background="@drawable/overlay_preview_background"
- android:clipChildren="true"
- android:clipToOutline="true"
- android:clipToPadding="true"
- android:layout_width="@dimen/clipboard_preview_size"
- android:layout_margin="@dimen/overlay_border_width"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- app:layout_constraintBottom_toBottomOf="@id/preview_border"
- app:layout_constraintStart_toStartOf="@id/preview_border"
- app:layout_constraintEnd_toEndOf="@id/preview_border"
- app:layout_constraintTop_toTopOf="@id/preview_border">
- <TextView android:id="@+id/text_preview"
- android:textFontWeight="500"
- android:padding="8dp"
- android:gravity="center|start"
- android:ellipsize="end"
- android:autoSizeTextType="uniform"
- android:autoSizeMinTextSize="@dimen/clipboard_overlay_min_font"
- android:autoSizeMaxTextSize="@dimen/clipboard_overlay_max_font"
- android:textColor="?attr/overlayButtonTextColor"
- android:textColorLink="?attr/overlayButtonTextColor"
- android:background="?androidprv:attr/colorAccentSecondary"
- android:layout_width="@dimen/clipboard_preview_size"
- android:layout_height="@dimen/clipboard_preview_size"/>
- <ImageView
- android:id="@+id/image_preview"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:contentDescription="@string/clipboard_image_preview"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <TextView
- android:id="@+id/hidden_preview"
- android:visibility="gone"
- android:textFontWeight="500"
- android:padding="8dp"
- android:gravity="center"
- android:textSize="14sp"
- android:textColor="?attr/overlayButtonTextColor"
- android:background="?androidprv:attr/colorAccentSecondary"
- android:layout_width="@dimen/clipboard_preview_size"
- android:layout_height="@dimen/clipboard_preview_size"/>
- </FrameLayout>
- <FrameLayout
- android:id="@+id/dismiss_button"
- android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
- android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
- android:elevation="10dp"
- android:visibility="gone"
- android:alpha="0"
- app:layout_constraintStart_toEndOf="@id/clipboard_preview"
- app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
- app:layout_constraintTop_toTopOf="@id/clipboard_preview"
- app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
- android:contentDescription="@string/clipboard_dismiss_description">
- <ImageView
- android:id="@+id/dismiss_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="@dimen/overlay_dismiss_button_margin"
- android:src="@drawable/overlay_cancel"/>
- </FrameLayout>
-</com.android.systemui.screenshot.DraggableConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
new file mode 100644
index 0000000..89d88fe
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="52dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:background="@drawable/keyguard_settings_popup_menu_background"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp"
+ android:paddingVertical="16dp">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:layout_marginEnd="16dp"
+ android:tint="?android:attr/textColorPrimary"
+ android:importantForAccessibility="no"
+ tools:ignore="UseAppTint" />
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:maxLines="1"
+ android:ellipsize="end" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index b76de5a..e182a6a 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,6 +24,7 @@
android:orientation="vertical">
<LinearLayout
+ android:id="@+id/media_metadata_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 159323a..a11ffcd 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -26,6 +26,11 @@
android:layout_height="match_parent"
android:background="@android:color/transparent">
+ <com.android.systemui.common.ui.view.LongPressHandlingView
+ android:id="@+id/keyguard_long_press"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<ViewStub
android:id="@+id/keyguard_qs_user_switch_stub"
android:layout="@layout/keyguard_qs_user_switch"
@@ -136,7 +141,7 @@
android:id="@+id/lock_icon_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/fingerprint_bg"
+ android:src="@drawable/fingerprint_bg"
android:visibility="invisible"/>
<ImageView
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index fcef0d2..7f12066 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Outomaties"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen klank of vibrasie nie"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen klank of vibrasie nie en verskyn laer in gespreksafdeling"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan lui of vibreer op grond van fooninstellings"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan lui of vibreer op grond van fooninstellings. Gesprekke van <xliff:g id="APP_NAME">%1$s</xliff:g> af verskyn by verstek in \'n borrel."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laat die stelsel bepaal of hierdie kennisgewing \'n klank moet maak of vibreer"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Bevorder na Verstek"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Gedegradeer na Stil"</string>
@@ -812,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Medium"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Klein"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Groot"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Klaar"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Wysig"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Vergrootglasvensterinstellings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tik om toeganklikheidkenmerke oop te maak Pasmaak of vervang knoppie in Instellings.\n\n"<annotation id="link">"Bekyk instellings"</annotation></string>
@@ -885,8 +886,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ontdoen"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Beweeg nader om op <xliff:g id="DEVICENAME">%1$s</xliff:g> te speel"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Beweeg nader aan <xliff:g id="DEVICENAME">%1$s</xliff:g> om hier te speel"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Speel tans op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Iets is fout. Probeer weer."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Laai tans"</string>
@@ -915,6 +915,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Luidsprekers en skerms"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde toestelle"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Saai uit"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mense in jou omtrek met versoenbare Bluetooth-toestelle kan na die media luister wat jy uitsaai"</string>
@@ -1056,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Hierdie skerm sal afskakel"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Voubare toestel word ontvou"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Voubare toestel word omgekeer"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batterykrag oor"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koppel jou stilus aan ’n laaier"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 9ae21c1..822f156 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"የታች ወሰን <xliff:g id="PERCENT">%1$d</xliff:g> በመቶ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"የግራ ወሰን <xliff:g id="PERCENT">%1$d</xliff:g> በመቶ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"የቀኝ ወሰን <xliff:g id="PERCENT">%1$d</xliff:g> በመቶ"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"የሥራ ቅጽበታዊ ገጽ እይታዎች በ<xliff:g id="APP">%1$s</xliff:g> መተግበሪያ ውስጥ ይቀመጣሉ"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ፋይሎች"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"የማያ ገጽ ቀረጻን በማሰናዳት ላይ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንድ የማያ ገጽ ቀረጻ ክፍለ-ጊዜ በመካሄድ ያለ ማሳወቂያ"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ራስ-ሰር"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ምንም ድምፅ ወይም ንዝረት የለም"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ምንም ድምፅ ወይም ንዝረት የለም እና በውይይት ክፍል ላይ አይታይም"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"በእርስዎ የስልክ ቅንብሮች የሚወሰን ሆኖ ሊደውል ወይም ሊነዝር ይችላል"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"በእርስዎ የስልክ ቅንብሮች የሚወሰን ሆኖ ሊደውል ወይም ሊነዝር ይችላል። የ<xliff:g id="APP_NAME">%1$s</xliff:g> አረፋ ውይይቶች በነባሪነት።"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ይህ ማሳወቂያ ድምፅ ወይም ንዝረት መደረግ ካለበት ስርዓቱ እንዲወሰን ያድርጉት"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ሁኔታ:</b> ለነባሪ ከፍ ተዋውቋል።"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ሁኔታ:</b> ወደ ዝምታ ዝቅ ተደርጓል"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"መካከለኛ"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"ትንሽ"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"ትልቅ"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"ተከናውኗል"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"አርትዕ"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"የማጉያ መስኮት ቅንብሮች"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"የተደራሽነት ባህሪያትን ለመክፈት መታ ያድርጉ። ይህንን አዝራር በቅንብሮች ውስጥ ያብጁ ወይም ይተኩ።\n\n"<annotation id="link">"ቅንብሮችን አሳይ"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ከ<xliff:g id="APP_LABEL">%2$s</xliff:g> ያጫውቱ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ቀልብስ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ ለማጫወት ጠጋ ያድርጉ"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"እዚህ ለማጫወት ወደ <xliff:g id="DEVICENAME">%1$s</xliff:g> ጠጋ ይበሉ"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ በማጫወት ላይ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"የሆነ ችግር ተፈጥሯል። እንደገና ይሞክሩ።"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"በመጫን ላይ"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ጡባዊ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"አልተገኘም"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"መቆጣጠሪያ አይገኝም"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"የድምጽ መጠን"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ድምጽ ማውጫዎች እና ማሳያዎች"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"የተጠቆሙ መሣሪያዎች"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ማሰራጨት እንዴት እንደሚሠራ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ስርጭት"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ይህ ማያ ገጽ ይጠፋል"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"መታጠፍ የሚችል መሣሪያ እየተዘረጋ ነው"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"መታጠፍ የሚችል መሣሪያ እየተገለበጠ ነው"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ባትሪ ይቀራል"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ብሮስፌዎን ከኃይል መሙያ ጋር ያገናኙ"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"የብሮስፌ ባትሪ ዝቅተኛ ነው"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 3e160c7..3daba41 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"الحد السفلى <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"الحد الأيسر <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"الحد الأيمن <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"يتم حفظ لقطات الشاشة الخاصة بالعمل في تطبيق \"<xliff:g id="APP">%1$s</xliff:g>\"."</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"الملفات"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"جارٍ معالجة تسجيل الشاشة"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"إشعار مستمر لجلسة تسجيل شاشة"</string>
@@ -449,7 +447,7 @@
<string name="monitoring_description_managed_profile_named_vpn" msgid="7254359257263069766">"تطبيقات العمل الخاصة بك متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>. يمكن لمشرف تكنولوجيا المعلومات ومزوّد خدمة الشبكة الافتراضية الخاصة (VPN) رؤية أنشطة الشبكة في تطبيقات العمل، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح."</string>
<string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"تطبيقاتك الشخصية متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>. تظهر أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح، لمزوّد خدمة الشبكة الافتراضية الخاصة (VPN)."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
- <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"فتح إعدادات الشبكة الافتراضية الخاصة (VPN)"</string>
+ <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"فتح إعدادات شبكة VPN"</string>
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"يتولّى أحد الوالدين إدارة هذا الجهاز. يمكن للوالدين عرض وإدارة معلوماتك، مثلاً التطبيقات التي تستخدمها وموقعك الجغرافي ووقت النظر إلى الشاشة."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"شبكة افتراضية خاصة"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"فتح القفل باستمرار بواسطة TrustAgent"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"تلقائي"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صوت أو اهتزاز"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"بدون صوت أو اهتزاز وتظهر في أسفل قسم المحادثات"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"يمكن إصدار رنين أو اهتزاز بناءً على إعدادات الهاتف."</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"يمكن إصدار رنين أو اهتزاز بناءً على إعدادات الهاتف. تظهر المحادثات من <xliff:g id="APP_NAME">%1$s</xliff:g> كفقاعات تلقائيًا."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"السماح للنظام بتحديد ما إذا يجب اهتزاز الجهاز أو إصدار رنين عند تلقّي هذا الإشعار"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>الحالة:</b> تمت الترقية إلى الإعداد التلقائي"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>الحالة:</b> تم خفض الترتيب إلى الوضع صامت"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"تشغيل <xliff:g id="SONG_NAME">%1$s</xliff:g> من تطبيق <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"تراجع"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"عليك الاقتراب لتشغيل الوسائط على <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"للتشغيل هنا، عليك الاقتراب من \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\"."</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"جارٍ تشغيل الوسائط على <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"حدث خطأ. يُرجى إعادة المحاولة."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"جارٍ التحميل"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"جهاز لوحي"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غير نشط، تحقّق من التطبيق."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"لم يتم العثور عليه."</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"عنصر التحكّم غير متوفّر"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"مستوى الصوت"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"مكبّرات الصوت والشاشات"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"الأجهزة المقترَحة"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"كيفية عمل البث"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"البث"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* سيتم إطفاء هذه الشاشة."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"جهاز قابل للطي يجري فتحه"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"جهاز قابل للطي يجري قلبه"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"النسبة المئوية المتبقية من شحن البطارية: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"عليك توصيل قلم الشاشة بشاحن."</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"بطارية قلم الشاشة منخفضة"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 7e330d2..16c9935 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"তলৰ সীমা <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"বাওঁফালৰ সীমা <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"সোঁফালৰ সীমা <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"<xliff:g id="APP">%1$s</xliff:g> এপ্টোত কৰ্মস্থানৰ স্ক্ৰীনশ্বটসমূহ ছেভ কৰা হয়"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ফাইল"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রীন ৰেকৰ্ডিঙৰ প্ৰক্ৰিয়াকৰণ হৈ আছে"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীন ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"স্বয়ংক্ৰিয়"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"কোনো ধ্বনি অথবা কম্পন নাই"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"কোনো ধ্বনি অথবা কম্পন নাই আৰু বাৰ্তালাপ শাখাটোৰ তলৰ অংশত দেখা পোৱা যায়"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ফ’নৰ ছেটিঙৰ ওপৰত নিৰ্ভৰ কৰি ৰিং কৰিব অথবা কম্পন হ’ব পাৰে"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ফ’নৰ ছেটিঙৰ ওপৰত নিৰ্ভৰ কৰি ৰিং কৰিব অথবা কম্পন হ’ব পাৰে। <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাৰ্তালাপ ডিফ’ল্ট হিচাপে বাবল হয়।"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"এই জাননীটোৱে ধ্বনি নে কম্পন সৃষ্টি কৰিব সেয়া ছিষ্টেমটোক নিৰ্ধাৰণ কৰিবলৈ দিয়ক"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>স্থিতি:</b> ডিফ’ল্টলৈ বৃদ্ধি কৰা হৈছে"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>স্থিতি:</b> নীৰৱলৈ হ্ৰাস কৰা হৈছে"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ত <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ কৰক"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"আনডু কৰক"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে’ কৰিবলৈ ওচৰলৈ যাওক"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে কৰি থকা হৈছে"</string>
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ইয়াত প্লে’ কৰিবলৈ, <xliff:g id="DEVICENAME">%1$s</xliff:g>ৰ ওচৰলৈ যাওক"</string>
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে\' কৰি থকা হৈছে"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"কিবা ভুল হ’ল। পুনৰ চেষ্টা কৰক।"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ল’ড হৈ আছে"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"টেবলেট"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"সক্ৰিয় নহয়, এপ্টো পৰীক্ষা কৰক"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"বিচাৰি পোৱা নগ’ল"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"নিয়ন্ত্ৰণটো উপলব্ধ নহয়"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ভলিউম"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"স্পীকাৰ আৰু ডিছপ্লে’"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"পৰামৰ্শ হিচাপে পোৱা ডিভাইচ"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"সম্প্ৰচাৰ কৰাটোৱে কেনেকৈ কাম কৰে"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্ৰচাৰ"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ই স্ক্ৰীনখন অফ হ’ব"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> বেটাৰী বাকী আছে"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"আপোনাৰ ষ্টাইলাছ এটা চাৰ্জাৰৰ সৈতে সংযোগ কৰক"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"ষ্টাইলাছৰ বেটাৰী কম আছে"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 192f236..0646697 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Aşağı sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sol sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sağ sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"İş skrinşotları <xliff:g id="APP">%1$s</xliff:g> tətbiqində saxlanılır"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fayllar"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Səs və ya vibrasiya yoxdur"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Söhbət siyahısının aşağısında səssiz və vibrasiyasız görünür"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Telefon ayarlarına əsasən zəng çala və ya titrəyə bilər"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Telefon ayarlarına əsasən zəng çala və ya vibrasiya edə bilər. <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqindən söhbətlərdə defolt olaraq qabarcıq çıxır."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirişin səs çıxarması və ya vibrasiya etməsi sistem tərəfindən təyin edilsin"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Defolt ayara keçirilib"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Səssiz rejimə keçirilib"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Orta"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Kiçik"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Böyük"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Hazırdır"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Redaktə edin"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Böyüdücü pəncərə ayarları"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Əlçatımlılıq funksiyalarını açmaq üçün toxunun. Ayarlarda bu düyməni fərdiləşdirin və ya dəyişdirin.\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%2$s</xliff:g> tətbiqindən oxudun"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Geri qaytarın"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxutmaq üçün yaxınlaşın"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Burada oxutmaq üçün <xliff:g id="DEVICENAME">%1$s</xliff:g> cihazına yaxınlaşdırın"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxudulur"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Xəta oldu. Yenə cəhd edin."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Yüklənir"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planşet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tapılmadı"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Nəzarət əlçatan deyil"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Səs"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Dinamiklər & Displeylər"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Təklif olunan Cihazlar"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayım necə işləyir"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Yayım"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Bu ekran deaktiv ediləcək"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Qatlana bilən cihaz açılır"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Qatlana bilən cihaz fırladılır"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> enerji qalıb"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Qələmi adapterə qoşun"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ae59e01f..9b8bbec 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Donja ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Leva ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Desna ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Snimci ekrana za posao se čuvaju u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fajlovi"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađujemo video snimka ekrana"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatska"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka i vibriranja"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka i vibriranja i prikazuje se u nastavku odeljka za konverzacije"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Može da zvoni ili vibrira u zavisnosti od podešavanja telefona"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Može da zvoni ili vibrira u zavisnosti od podešavanja telefona. Konverzacije iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> se podrazumevano prikazuju u oblačićima."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem utvrdi da li ovo obaveštenje treba da emituje zvuk ili da vibrira"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Unapređeno u Podrazumevano"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Degradirano u Nečujno"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Srednje"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Malo"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Veliko"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Gotovo"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Izmeni"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Podešavanja prozora za uvećanje"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za funkcije pristupačnosti. Prilagodite ili zamenite ovo dugme u Podešavanjima.\n\n"<annotation id="link">"Podešavanja"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Opozovi"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Približite da biste puštali muziku na: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Da biste puštali sadržaj ovde, približite uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Pušta se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Došlo je do greške. Probajte ponovo."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Učitava se"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrola nije dostupna"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Zvuk"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcioniše emitovanje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emitovanje"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ovaj ekran će se isključiti"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Uređaj na preklop se otvara"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Uređaj na preklop se obrće"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je još<xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisaljku sa punjačem"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Nizak nivo baterije pisaljke"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 53f2ff0..3a476f4 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ніжняя граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Левая граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Правая граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Працоўныя здымкі экрана захаваны ў праграме \"<xliff:g id="APP">%1$s</xliff:g>\""</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Файлы"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Апрацоўваецца запіс экрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Бягучае апавяшчэнне для сеанса запісу экрана"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Аўтаматычна"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без гуку ці вібрацыі"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Паказваецца без гуку ці вібрацыі ў раздзеле размоў"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя. Размовы ў праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартна паяўляюцца ў выглядзе ўсплывальных апавяшчэнняў."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Сістэма сама будзе вызначаць, ці трэба для гэтага апавяшчэння ўключаць гук або вібрацыю"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Стан:</b> Пазначана як стандартнае"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Стан:</b> Пераведзена ў рэжым \"Без гуку\""</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Сярэдні"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Дробны"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Вялікі"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Гатова"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Змяніць"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Налады акна лупы"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Націсніце, каб адкрыць спецыяльныя магчымасці. Рэгулюйце ці замяняйце кнопку ў Наладах.\n\n"<annotation id="link">"Прагляд налад"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Прайграйце кампазіцыю \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" з дапамогай праграмы \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Адрабіць"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Каб прайграць мультымедыя на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", наблізьцеся да яе"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Каб прайграць тут, падыдзіце бліжэй да прылады \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Прайграецца на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Нешта пайшло не так. Паўтарыце спробу."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Ідзе загрузка"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"планшэт"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактыўна, праверце праграму"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знойдзена"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Кіраванне недаступнае"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Гучнасць"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Дынамікі і дысплэі"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Прылады, якія падтрымліваюцца"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як адбываецца трансляцыя"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Трансляцыя"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Гэты экран будзе выключаны"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Складная прылада ў раскладзеным выглядзе"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Перавернутая складная прылада"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Засталося зараду: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Падключыце пяро да зараднай прылады"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Нізкі ўзровень зараду пяра"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 71fdfa9..de24b94 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Долна граница: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Лява граница: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Дясна граница: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Екранните снимки, направени в служебния потребителски профил, се запазват в приложението „<xliff:g id="APP">%1$s</xliff:g>“"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Записът на екрана се обработва"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибриране"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибриране и се показва по-долу в секцията с разговори"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да звъни или да вибрира въз основа на настройките за телефона"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да звъни или да вибрира според настройките за телефона. Разговорите от <xliff:g id="APP_NAME">%1$s</xliff:g> се показват като балончета по подразбиране."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Нека системата да определя дали дадено известие да се придружава от звук, или вибриране"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Състояние:</b> Повишено до основно"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Състояние:</b> Понижено до беззвучно"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пускане на <xliff:g id="SONG_NAME">%1$s</xliff:g> от <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Отмяна"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Преместете се по-близо, за да се възпроизведе на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"За възпроизвеждане тук се приближете до <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Възпроизвежда се на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Нещо се обърка. Опитайте отново."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Зарежда се"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, проверете прилож."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не е намерено"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Контролата не е налице"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Сила на звука"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Високоговорители и екрани"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени устройства"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работи предаването"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Предаване"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Този екран ще се изключи"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Разгъване на сгъваемо устройство"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Обръщане на сгъваемо устройство"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Оставаща батерия: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Свържете писалката към зарядно устройство"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Батерията на писалката е изтощена"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 9a91787..a72733c 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"অটোমেটিক"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"আওয়াজ করবে না বা ভাইব্রেট হবে না"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"আওয়াজ করবে না বা ভাইব্রেট হবে না এবং কথোপকথন বিভাগের নিচের দিকে দেখা যাবে"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ফোনের সেটিংস অনুযায়ী ফোন রিং বা ভাইব্রেট হতে পারে"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ফোনের সেটিংস অনুযায়ী ফোন রিং বা ভাইব্রেট হতে পারে। <xliff:g id="APP_NAME">%1$s</xliff:g>-এর কথোপকথন সাধারণত বাবলের মতো দেখাবে।"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"এই বিজ্ঞপ্তি এলে ডিভাইস আওয়াজ করবে না ভাইব্রেট করবে তা সিস্টেমকে সেট করতে দিন"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>স্ট্যাটাস:</b> লেভেল বাড়িয়ে ডিফল্ট করা হয়েছে"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>স্ট্যাটাস:</b> লেভেল কমিয়ে সাইলেন্ করা হয়েছে"</string>
@@ -885,8 +887,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%2$s</xliff:g> অ্যাপে চালান"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"আগের অবস্থায় ফিরুন"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ চালাতে আরও কাছে আনুন"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"এখানে চালানোর জন্য আপনার ডিভাইস <xliff:g id="DEVICENAME">%1$s</xliff:g>-এর কাছে নিয়ে যান"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ ভিডিও চালানো হচ্ছে"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"কোনও সমস্যা হয়েছে। আবার চেষ্টা করুন।"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"লোড করা হচ্ছে"</string>
@@ -915,6 +916,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"স্পিকার & ডিসপ্লে"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"সাজেস্ট করা ডিভাইস"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ব্রডকাস্ট কীভাবে কাজ করে"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্রচার করুন"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"আশপাশে লোকজন যাদের মানানসই ব্লুটুথ ডিভাইস আছে, তারা আপনার ব্রডকাস্ট করা মিডিয়া শুনতে পারবেন"</string>
@@ -1056,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ এই স্ক্রিন বন্ধ হয়ে যাবে"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ফোল্ড করা যায় এমন ডিভাইস খোলা হচ্ছে"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ফোল্ড করা যায় এমন ডিভাইস উল্টানো হচ্ছে"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ব্যাটারির চার্জ বাকি আছে"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"কোনও চার্জারের সাথে আপনার স্টাইলাস কানেক্ট করুন"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"স্টাইলাস ব্যাটারিতে চার্জ কম আছে"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index ef0f703..d82e269 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka ili vibracije"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka ili vibracije i pojavljuje se pri dnu odjeljka razgovora"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Može zvoniti ili vibrirati na osnovu postavki telefona"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Može zvoniti ili vibrirati na osnovu postavki telefona. Razgovori iz oblačića u aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> kao zadana opcija."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Možda će zvoniti ili vibrirati, ovisno o postavkama uređaja"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Možda će zvoniti ili vibrirati, ovisno o postavkama uređaja. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačiću prema zadanim postavkama."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem odluči treba li se ovo obavještenje oglasiti zvukom ili vibracijom"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> je unaprijeđen u Zadano"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> je unazađen u Nečujno"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Srednje"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Malo"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Veliko"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Gotovo"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Uredi"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Postavke prozora povećala"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite da otvorite funkcije pristupačnosti. Prilagodite ili zamijenite dugme u Postavkama.\n\n"<annotation id="link">"Postavke"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Približite da reproducirate na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Da reproducirate ovdje, približite se uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Nešto nije uredu. Pokušajte ponovo."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Učitavanje"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Zahtijeva račun s naplatom"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcionira emitiranje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emitirajte"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u vašoj blizini s kompatibilnim Bluetooth uređajima mogu slušati medijske sadržaje koje emitirate"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ekran će se isključiti"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Sklopivi uređaj se rasklapa"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Sklopivi uređaj se obrće"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nije moguće uspostavljati pozive s ovog profila"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Vaša pravila za poslovne uređaje omogućuju vam upućivanje poziva samo s poslovnog profila"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Prijeđite na poslovni profil"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zatvori"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index e41292c..308ea11 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Marge inferior <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Marge esquerre <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Marge dret <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Les captures de pantalla de treball es desen a l\'aplicació <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fitxers"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automàtic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sense so ni vibració"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sense so ni vibració i es mostra més avall a la secció de converses"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pot sonar o vibrar en funció de la configuració del telèfon"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pot sonar o vibrar en funció de la configuració del telèfon. Les converses de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> es mostren com a bombolles de manera predeterminada."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fes que el sistema determini si aquesta notificació ha d\'emetre un so o una vibració"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Estat</b>: s\'ha augmentat a Predeterminat"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Estat</b>: s\'ha disminuït a Silenci"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> des de l\'aplicació <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfés"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Mou més a prop per reproduir a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Per reproduir contingut aquí, apropa\'l a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"S\'està reproduint a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"S\'ha produït un error. Torna-ho a provar."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"S\'està carregant"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tauleta"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No s\'ha trobat"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"El control no està disponible"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altaveus i pantalles"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositius suggerits"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Com funciona l\'emissió"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emet"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Aquesta pantalla s\'apagarà"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositiu plegable desplegant-se"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositiu plegable girant"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de bateria"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connecta el llapis òptic a un carregador"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 1bf8f2e..2a01318 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Dolní okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Levý okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Pravý okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Pracovní snímky obrazovky se ukládají do aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Soubory"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Žádný zvuk ani vibrace"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Žádný zvuk ani vibrace a zobrazuje se níže v sekci konverzací"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Vyzvání nebo vibruje podle nastavení telefonu"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Vyzvání nebo vibruje podle nastavení telefonu. Konverzace z aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> mají ve výchozím nastavení podobu bublin."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Vyzvání nebo vibruje podle nastavení zařízení"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Vyzvání nebo vibruje podle nastavení zařízení. Konverzace z aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> ve výchozím nastavení bublají."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechat systém rozhodnout, zda má toto oznámení vydat zvuk či zavibrovat"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stav:</b> priorita zvýšena na Výchozí"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Stav:</b> priorita snížena na Tiché"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Střední"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Malý"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Velký"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Hotovo"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Upravit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavení okna zvětšení"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Klepnutím otevřete funkce přístupnosti. Tlačítko lze upravit nebo nahradit v Nastavení.\n\n"<annotation id="link">"Nastavení"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Přehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Vrátit zpět"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Pokud chcete přehrávat na zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>, přibližte se k němu"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pokud obsah chcete přehrát na tomto zařízení, přesuňte ho blíže k zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Přehrávání v zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Došlo k chybě. Zkuste to znovu."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Načítání"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenalezeno"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Ovládání není k dispozici"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hlasitost"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a displeje"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Navrhovaná zařízení"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Vyžaduje prémiový účet"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak vysílání funguje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Vysílání"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lidé ve vašem okolí s kompatibilními zařízeními Bluetooth mohou poslouchat média, která vysíláte"</string>
@@ -1060,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Tato obrazovka se vypne"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rozkládání rozkládacího zařízení"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Otáčení rozkládacího zařízení"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g> baterie"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Připojte dotykové pero k nabíječce"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Slabá baterie dotykového pera"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Z tohoto profilu nelze volat"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Vaše pracovní zásady vám umožňují telefonovat pouze z pracovního profilu"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Přepnout na pracovní profil"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zavřít"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c560843..d7467b00 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nederste kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Venstre kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Højre kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Screenshots, der tages via arbejdsprofilen, gemmer i appen <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Filer"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skærmoptagelse"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ingen lyd eller vibration, og den vises længere nede i samtalesektionen"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringe eller vibrere baseret på telefonens indstillinger"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringe eller vibrere baseret på telefonens indstillinger. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard i bobler."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Få systemet til at afgøre, om denne notifikation skal vibrere eller afspille en lyd"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Angivet som Standard"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Angivet som Lydløs"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Fortryd"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ryk tættere på for at afspille på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"For at afspille her skal enheden tættere på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Afspilles på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Noget gik galt. Prøv igen."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Indlæser"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke fundet"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Styringselement ikke tilgængeligt"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Lydstyrke"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Højttalere og skærme"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåede enheder"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Sådan fungerer udsendelser"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Udsendelse"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ *Denne skærm slukkes"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldbar enhed foldes ud"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldbar enhed vendes om"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri tilbage"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Slut din styluspen til en oplader"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4013a3d..0b01f15 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Unterer Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linker Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Rechter Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Mit einem Arbeitsprofil aufgenommene Screenshots werden in der App „<xliff:g id="APP">%1$s</xliff:g>“ gespeichert"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Dateien"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Bildschirmaufzeichnung…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Kein Ton und keine Vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Kein Ton und keine Vibration, erscheint weiter unten im Bereich „Unterhaltungen“"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kann klingeln oder vibrieren, abhängig von den Telefoneinstellungen"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kann klingeln oder vibrieren, je nach Telefoneinstellungen. Unterhaltungen von <xliff:g id="APP_NAME">%1$s</xliff:g> werden standardmäßig als Bubble angezeigt."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Das System entscheiden lassen, ob bei dieser Benachrichtigung ein Ton oder eine Vibration ausgegeben wird"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status</b>: auf „Standard“ hochgestuft"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status</b>: auf „Lautlos“ herabgestuft"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> über <xliff:g id="APP_LABEL">%2$s</xliff:g> wiedergeben"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Rückgängig machen"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Gehe für die Wiedergabe näher an „<xliff:g id="DEVICENAME">%1$s</xliff:g>“ heran"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Für eine Wiedergabe auf diesem Gerät muss es näher bei <xliff:g id="DEVICENAME">%1$s</xliff:g> sein"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Wiedergabe läuft auf „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
- <string name="media_transfer_failed" msgid="7955354964610603723">"Es gab ein Problem. Versuch es noch einmal."</string>
+ <string name="media_transfer_failed" msgid="7955354964610603723">"Ein Fehler ist aufgetreten. Versuch es noch einmal."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Wird geladen"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"Tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nicht gefunden"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Steuerelement nicht verfügbar"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Lautstärke"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Lautsprecher & Displays"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vorgeschlagene Geräte"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Funktionsweise von Nachrichten an alle"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Nachricht an alle"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Dieses Display wird dann ausgeschaltet"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Faltbares Gerät wird geöffnet"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Faltbares Gerät wird umgeklappt"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akku bei <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c1188ed..0db8cfb 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Αυτόματο"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Χωρίς ήχο ή δόνηση"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Χωρίς ήχο ή δόνηση και εμφανίζεται χαμηλά στην ενότητα συζητήσεων"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου. Οι συζητήσεις από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εμφανίζονται σε συννεφάκι από προεπιλογή."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Επιτρέψτε στο σύστημα να αποφασίσει αν αυτή η ειδοποίηση θα αναπαράγει έναν ήχο ή θα ενεργοποιήσει τη δόνηση της συσκευής"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Κατάσταση:</b> Προάχθηκε σε Προεπιλογή"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Κατάσταση:</b> Υποβιβάστηκε σε Αθόρυβη"</string>
@@ -885,8 +887,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Αναπαραγωγή του <xliff:g id="SONG_NAME">%1$s</xliff:g> στην εφαρμογή <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Αναίρεση"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Πλησιάστε για αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Για να γίνει αναπαραγωγή εδώ, μετακινηθείτε πιο κοντά στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Παρουσιάστηκε κάποιο πρόβλημα. Δοκιμάστε ξανά."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Φόρτωση"</string>
@@ -915,6 +916,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Ηχεία και οθόνες"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Προτεινόμενες συσκευές"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Πώς λειτουργεί η μετάδοση"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Μετάδοση"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Οι άνθρωποι με συμβατές συσκευές Bluetooth που βρίσκονται κοντά σας μπορούν να ακούσουν το μέσο που μεταδίδετε."</string>
@@ -1056,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Αυτή η οθόνη θα απενεργοποιηθεί"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Αναδιπλούμενη συσκευή που ξεδιπλώνει"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Αναδιπλούμενη συσκευή που διπλώνει"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Απομένει το <xliff:g id="PERCENTAGE">%s</xliff:g> της μπαταρίας"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Συνδέστε τη γραφίδα σε έναν φορτιστή"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Χαμηλή στάθμη μπαταρίας γραφίδας"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index dc4eecc..8f6b9b9 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> promoted to default"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> demoted to silent"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Medium"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Small"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Large"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Done"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Edit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -1058,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Can\'t call from this profile"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3fa3fa7..8c4e66c 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Promoted to Default"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Demoted to Silent"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Medium"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Small"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Large"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Done"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Edit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customize or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
@@ -914,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & Displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested Devices"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting"</string>
@@ -1057,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Can\'t call from this profile"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index dc4eecc..8f6b9b9 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> promoted to default"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> demoted to silent"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Medium"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Small"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Large"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Done"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Edit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -1058,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Can\'t call from this profile"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index dc4eecc..8f6b9b9 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> promoted to default"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> demoted to silent"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Medium"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Small"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Large"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Done"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Edit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -1058,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Can\'t call from this profile"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 0a3cf41..34bf569 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No sound or vibration and appears lower in conversation section"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"May ring or vibrate based on phone settings"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"May ring or vibrate based on phone settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Promoted to Default"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Demoted to Silent"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Medium"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Small"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Large"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Done"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Edit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customize or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation>""</string>
@@ -914,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & Displays"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested Devices"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting"</string>
@@ -1057,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Can\'t call from this profile"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8af2620..d4bbb3f 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Límite inferior: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Límite izquierdo: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Límite derecho: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Las capturas de pantalla de trabajo se guardan en la app de <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación constante para una sesión de grabación de pantalla"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones."</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono."</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puede sonar o vibrar en función de la configuración del teléfono. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Dejar que el sistema determine si esta notificación debe emitir un sonido o una vibración"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Estado:</b> Se promovió a Predeterminada"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Estado:</b> Descendió de nivel a Silenciada"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Mediano"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Pequeño"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Grande"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Listo"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Editar"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuración de la ventana de ampliación"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Presiona para abrir las funciones de accesibilidad. Personaliza o cambia botón en Config.\n\n"<annotation id="link">"Ver config"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducir <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Acércate para reproducir en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproducir aquí, acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Se produjo un error. Vuelve a intentarlo."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se encontró"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"El control no está disponible"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bocinas y pantallas"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la transmisión"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmisión"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta pantalla se apagará"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo plegable siendo desplegado"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo plegable siendo girado"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batería restante"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu pluma stylus a un cargador"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 4437160..db0b4b0 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite inferior"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite izquierdo"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite derecho"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Las capturas de pantalla de trabajo se guardan en la aplicación <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Archivos"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación continua de una sesión de grabación de la pantalla"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración, y se muestra más abajo en la sección de conversaciones"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar según los ajustes del teléfono"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puede sonar o vibrar según los ajustes del teléfono. Las conversaciones de <xliff:g id="APP_NAME">%1$s</xliff:g> aparecen como burbujas de forma predeterminada."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Haz que el sistema determine si con esta notificación el dispositivo debe sonar o vibrar"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Estado:</b> cambio a Predeterminado"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Estado:</b> cambio a Silencio"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Mediano"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Pequeño"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Grande"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Listo"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Editar"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuración de la ventana de la lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca para abrir funciones de accesibilidad. Personaliza o sustituye este botón en Ajustes.\n\n"<annotation id="link">"Ver ajustes"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para reproducir contenido ahí"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproducirlo, acércate al dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>)"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Se ha producido un error. Inténtalo de nuevo."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se ha encontrado"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Control no disponible"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altavoces y pantallas"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Sugerencias de dispositivos"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emisión"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta pantalla se apagará"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo plegable desplegándose"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo plegable mostrado desde varios ángulos"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu lápiz óptico a un cargador"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3b6a0af..8171ddd9 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alapiir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vasak piir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Parem piir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Töö ekraanipildid salvestatakse rakendusse <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekraanisalvestuse töötlemine"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaatne"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ilma heli ja vibreerimiseta"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ilma heli ja vibreerimiseta, kuvatakse vestluste jaotises allpool"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Võib telefoni seadete põhjal heliseda või vibreerida"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Võib telefoni seadete põhjal heliseda või vibreerida. Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> vestlused kuvatakse vaikimisi mullis."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laske süsteemil määrata, kas selle märguande puhul peaks esitama heli või vibreerima"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Olek:</b> määrati prioriteet Vaikimisi"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Olek:</b> määrati prioriteet Vaikne"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Võta tagasi"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Liikuge lähemale, et seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g> esitada"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Siin esitamiseks minge seadmele <xliff:g id="DEVICENAME">%1$s</xliff:g> lähemale"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Esitatakse seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Midagi läks valesti. Proovige uuesti."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Laadimine"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tahvelarvuti"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei leitud"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Juhtelement pole saadaval"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Helitugevus"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kõlarid ja ekraanid"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Soovitatud seadmed"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kuidas ülekandmine toimib?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Ülekanne"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ See ekraan lülitatakse välja"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Volditava seadme lahtivoltimine"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Volditava seadme ümberpööramine"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akutase on <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ühendage elektronpliiats laadijaga"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 783a157..6f49d06 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Beheko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Ezkerreko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Eskuineko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Laneko pantaila-argazkiak <xliff:g id="APP">%1$s</xliff:g> aplikazioan gordetzen dira"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fitxategiak"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatikoa"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ez du tonurik jotzen edo dar-dar egiten"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ez du tonurik jotzen edo dar-dar egiten, eta elkarrizketen atalaren behealdean agertzen da"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Tonua joko du, edo dar-dar egingo, telefonoaren ezarpenen arabera"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Tonua joko du, edo dar-dar egingo, telefonoaren ezarpenen arabera. Modu lehenetsian, <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioko elkarrizketak burbuila gisa agertzen dira."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ezarri sistemak zehaztu dezala jakinarazpen honek soinua edo dardara egin behar duen ala ez"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"Lehenetsi gisa ezarri da <b>egoera:</b>"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"Soinurik gabeko modura aldatu da <b>egoera:</b>"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> bidez"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desegin"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Gertura ezazu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailuan erreproduzitzeko"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Hemen erreproduzitzeko, hurbildu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailura"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> gailuan erreproduzitzen"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Arazoren bat izan da. Saiatu berriro."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Kargatzen"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tableta"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktibo; egiaztatu aplikazioa"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ez da aurkitu"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Ez dago erabilgarri kontrolatzeko aukera"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Bolumena"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%% <xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bozgorailuak eta pantailak"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Iradokitako gailuak"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Iragarri"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Pantaila itzali egingo da"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Gailu tolesgarria zabaltzen"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Gailu tolesgarria biratzen"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateriaren <xliff:g id="PERCENTAGE">%s</xliff:g> geratzen da"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Konektatu arkatza kargagailu batera"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index df99cdf..f46d67b 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"مرز پایین <xliff:g id="PERCENT">%1$d</xliff:g> درصد"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"مرز سمت چپ <xliff:g id="PERCENT">%1$d</xliff:g> درصد"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"مرز سمت راست <xliff:g id="PERCENT">%1$d</xliff:g> درصد"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"نماگرفتهای نمایه کاری در برنامه <xliff:g id="APP">%1$s</xliff:g> ذخیره میشوند"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ضبطکننده صفحهنمایش"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"درحال پردازش ضبط صفحهنمایش"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اعلان درحال انجام برای جلسه ضبط صفحهنمایش"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صدا یا لرزش"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"بدون صدا و لرزش در پایین بخش مکالمه نشان داده میشود"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"بسته به تنظیمات ممکن است تلفن زنگ بزند یا لرزش داشته باشد"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"بسته به تنظیمات ممکن است تلفن زنگ بزند یا لرزش داشته باشد. مکالمههای <xliff:g id="APP_NAME">%1$s</xliff:g> بهطور پیشفرض در حبابک نشان داده میشوند."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"بسته به تنظیمات دستگاه ممکن است زنگ بزند یا بلرزد"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"بسته به تنظیمات دستگاه ممکن است زنگ بزند یا بلرزد. مکالمههای <xliff:g id="APP_NAME">%1$s</xliff:g> بهطور پیشفرض در حبابک نشان داده میشوند."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سیستم را تنظیم کنید که تشخیص دهد اعلان صدا و لرزش داشته باشد یا نه"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>وضعیت:</b> به «پیشفرض» ارتقا یافت"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>وضعیت:</b> به «بیصدا» تنزل یافت"</string>
@@ -887,13 +885,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> را ازطریق <xliff:g id="APP_LABEL">%2$s</xliff:g> پخش کنید"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"واگرد"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"برای پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g> به دستگاه نزدیکتر شوید"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"برای پخش در اینجا، به <xliff:g id="DEVICENAME">%1$s</xliff:g> نزدیکتر شوید"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"درحال پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"مشکلی پیش آمد. دوباره امتحان کنید."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"درحال بار کردن"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"رایانه لوحی"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غیرفعال، برنامه را بررسی کنید"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"پیدا نشد"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"کنترل دردسترس نیست"</string>
@@ -917,8 +913,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"میزان صدا"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"بلندگوها و نمایشگرها"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"دستگاههای پیشنهادی"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"حساب ممتاز لازم است"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"همهفرتستی چطور کار میکند"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"همهفرستی"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"افرادی که در اطرافتان دستگاههای Bluetooth سازگار دارند میتوانند به رسانهای که همهفرستی میکنید گوش کنند"</string>
@@ -1060,8 +1056,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ این صفحهنمایش خاموش خواهد شد"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"دستگاه تاشو درحال باز شدن"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"دستگاه تاشو درحال چرخش به اطراف"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> باتری باقی مانده است"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"قلم را به شارژر وصل کنید"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"باتری قلم ضعیف است"</string>
+ <string name="video_camera" msgid="7654002575156149298">"دوربین ویدیویی"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"نمیتوانید از این نمایه تماس بگیرید"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"خطمشی کاری شما فقط به برقراری تماس ازطریق نمایه کاری اجازه میدهد"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"رفتن به نمایه کاری"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"بستن"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 879508c..ffb420d 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alareuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vasen reuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Oikea reuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Työprofiilin kuvakaappaukset tallennetaan sovellukseen: <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaattinen"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ei ääntä tai värinää"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ei ääntä tai värinää ja näkyy alempana keskusteluosiossa"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Voi soida tai väristä puhelimen asetuksista riippuen"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Voi soida tai väristä puhelimen asetuksista riippuen. Näistä keskusteluista (<xliff:g id="APP_NAME">%1$s</xliff:g>) syntyy oletuksena kuplia."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Järjestelmä valitsee, kuuluuko tästä ilmoituksesta ääntä tai väriseekö se"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Tila:</b> valittu oletusarvoiseksi"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Tila:</b> hiljennetty"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="APP_LABEL">%2$s</xliff:g>)"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Kumoa"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Siirry lähemmäs, jotta <xliff:g id="DEVICENAME">%1$s</xliff:g> voi toistaa tämän"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Siirry lähemmäs laitetta (<xliff:g id="DEVICENAME">%1$s</xliff:g>) toistaaksesi täällä"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Toistetaan: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Jotain meni pieleen. Yritä uudelleen."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Latautuminen"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tabletti"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei löydy"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Ohjain ei ole käytettävissä"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Äänenvoimakkuus"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kaiuttimet ja näytöt"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ehdotetut laitteet"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Miten lähetys toimii"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Lähetys"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Tämä näyttö sammutetaan"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Taitettava laite taitetaan"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Taitettava laite käännetään ympäri"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkua jäljellä <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Yhdistä näyttökynä laturiin"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 39ab915..006cce8 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inférieure : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite gauche : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite droite : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Les captures d\'écran du profil professionnel sont enregistrées dans l\'application <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fichiers"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Aucun son ni vibration"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Aucun son ni vibration, et s\'affiche plus bas dans la section des conversations"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Peut sonner ou vibrer, selon les paramètres du téléphone"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Peut sonner ou vibrer, selon les paramètres du téléphone. Conversations des bulles de <xliff:g id="APP_NAME">%1$s</xliff:g> par défaut."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faire en sorte que le système détermine si cette notification devrait émettre un son ou vibrer"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>État :</b> élevé à la catégorie Par défaut"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>État :</b> abaissé à la catégorie Silencieux"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Rapprochez-vous pour faire jouer le contenu sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pour faire jouer le contenu ici, rapprochez-vous de <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_transfer_failed" msgid="7955354964610603723">"Un problème est survenu. Réessayez."</string>
+ <string name="media_transfer_failed" msgid="7955354964610603723">"Une erreur s\'est produite. Réessayez."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Chargement en cours…"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablette"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"La commande n\'est pas accessible"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Haut-parleurs et écrans"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement de la diffusion"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Diffusion"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Cet écran va s\'éteindre"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable en cours de dépliage"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable en train d\'être retourné"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Charge restante de la pile : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index a8e0cb4..d9267c9 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inférieure : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite gauche : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite droite : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Les captures d\'écran du profil professionnel sont enregistrées dans l\'appli <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fichiers"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Enregistrement de l\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement de l\'écran"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ni son, ni vibreur"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ni son, ni vibreur ; s\'affiche plus bas dans la section des conversations"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Son ou vibreur, selon les paramètres du téléphone"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Son ou vibreur, selon les paramètres du téléphone. Les conversations provenant de <xliff:g id="APP_NAME">%1$s</xliff:g> s\'affichent sous forme de bulles par défaut."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laisser le système déterminer si cette notification doit être accompagnée d\'un son ou d\'une vibration"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>État :</b> Élevée à la catégorie \"Par défaut\""</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>État :</b> Abaissée à la catégorie \"Silencieux\""</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> depuis <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Rapprochez-vous de votre <xliff:g id="DEVICENAME">%1$s</xliff:g> pour y lire le contenu"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pour lancer la lecture ici, rapprochez-vous de <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>…"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Un problème est survenu. Réessayez."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Chargement…"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablette"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Commande indisponible"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Enceintes et écrans"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Annonce"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Cet écran sera désactivé"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable qui est déplié"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable qui est retourné"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batterie restante"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index e180f60..a06ddd7 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bordo inferior: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Bordo esquerdo: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Bordo dereito: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"As capturas de pantalla do perfil de traballo gárdanse na aplicación <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Ficheiros"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando gravación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación en curso sobre unha sesión de gravación de pantalla"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sen son nin vibración, e aparecen máis abaixo na sección de conversas"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"O teléfono pode soar ou vibrar en función da súa configuración"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Poderían facer que o teléfono soe ou vibre en función da súa configuración. Conversas desde a burbulla da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai que o sistema determine se a notificación debe emitir un son ou unha vibración"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Estado:</b> ascendeuse a Predeterminada"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Estado:</b> o nivel diminuíuse a Silencioso"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfacer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Achega o dispositivo para reproducir o contido en: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproducir o contido aquí, achégate ao dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>)"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproducindo contido noutro dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>)"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Produciuse un erro. Téntao de novo."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tableta"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Non se atopou"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"O control non está dispoñible"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altofalantes e pantallas"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos suxeridos"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funcionan as difusións?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Difusión"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Desactivarase esta pantalla"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pregable abríndose"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pregable xirando"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta o lapis óptico a un cargador"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index d866a96..687d1af 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"નીચેની સીમા <xliff:g id="PERCENT">%1$d</xliff:g> ટકા"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ડાબી બાજુની સીમા <xliff:g id="PERCENT">%1$d</xliff:g> ટકા"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"જમણી બાજુની સીમા <xliff:g id="PERCENT">%1$d</xliff:g> ટકા"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"ઑફિસના સ્ક્રીનશૉટ <xliff:g id="APP">%1$s</xliff:g> ઍપમાં સાચવવામાં આવે છે"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ફાઇલો"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકોર્ડર"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"સ્ક્રીન રેકૉર્ડિંગ ચાલુ છે"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"સ્ક્રીન રેકોર્ડિંગ સત્ર માટે ચાલુ નોટિફિકેશન"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ઑટોમૅટિક રીતે"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"કોઈપણ સાઉન્ડ અથવા વાઇબ્રેશન નથી"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"કોઈપણ સાઉન્ડ અથવા વાઇબ્રેશન નથી અને વાતચીત વિભાગમાં તે વધુ નીચેની દિશાએ દેખાય છે"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ફોન સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ફોન સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે. ડિફૉલ્ટ તરીકે <xliff:g id="APP_NAME">%1$s</xliff:g> બબલની વાતચીત."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"ડિવાઇસના સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ડિવાઇસના સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે. ડિફૉલ્ટ તરીકે <xliff:g id="APP_NAME">%1$s</xliff:g> બબલની વાતચીત."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"આ નોટિફિકેશન સાઉન્ડ અથવા વાઇબ્રેટ કરી શકશે કે નહીં તે સિસ્ટમને નક્કી કરવા દો"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>સ્ટેટસ:</b> ડિફૉલ્ટ તરીકે બઢતી આપવામાં આવી"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>સ્ટેટસ:</b> સાઇલન્ટ પર અવનત કરવામાં આવ્યું"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"મધ્યમ"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"નાનું"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"મોટું"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"થઈ ગયું"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"ફેરફાર કરો"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"મેગ્નિફાયર વિન્ડોના સેટિંગ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ઍક્સેસિબિલિટી સુવિધાઓ ખોલવા માટે ટૅપ કરો. સેટિંગમાં આ બટનને કસ્ટમાઇઝ કરો અથવા બદલો.\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> પર <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચલાવો"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"છેલ્લો ફેરફાર રદ કરો"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવા માટે વધુ નજીક ખસેડો"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"અહીં ચલાવવા માટે, <xliff:g id="DEVICENAME">%1$s</xliff:g>ની નજીક લાવો"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવામાં આવી રહ્યું છે"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"કંઈક ખોટું થયું. ફરી પ્રયાસ કરો."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"લોડ થઈ રહ્યું છે"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ટૅબ્લેટ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"નિષ્ક્રિય, ઍપને ચેક કરો"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"મળ્યું નથી"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"નિયંત્રણ ઉપલબ્ધ નથી"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"વૉલ્યૂમ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"સ્પીકર અને ડિસ્પ્લે"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"સૂચવેલા ડિવાઇસ"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium એકાઉન્ટ જરૂરી છે"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"બ્રોડકાસ્ટ પ્રક્રિયાની કામ કરવાની રીત"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"બ્રોડકાસ્ટ કરો"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"સુસંગત બ્લૂટૂથ ડિવાઇસ ધરાવતા નજીકના લોકો તમે જે મીડિયા બ્રોડકાસ્ટ કરી રહ્યાં છો તે સાંભળી શકે છે"</string>
@@ -1060,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ આ સ્ક્રીન બંધ થઈ જશે"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ફોલ્ડ કરી શકાય એવું ડિવાઇસ અનફોલ્ડ કરવામાં આવી રહ્યું છે"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ફોલ્ડ કરી શકાય એવું ડિવાઇસ ફ્લિપ કરવામાં આવી રહ્યું છે"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> બૅટરી બાકી છે"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"તમારા સ્ટાઇલસને ચાર્જર સાથે કનેક્ટ કરો"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"સ્ટાઇલસની બૅટરીમાં ચાર્જ ઓછો છે"</string>
+ <string name="video_camera" msgid="7654002575156149298">"વીડિયો કૅમેરા"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"આ પ્રોફાઇલ પરથી કૉલ કરી શકતા નથી"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"તમારી ઑફિસની પૉલિસી તમને માત્ર ઑફિસની પ્રોફાઇલ પરથી જ ફોન કૉલ કરવાની મંજૂરી આપે છે"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"ઑફિસની પ્રોફાઇલ પર સ્વિચ કરો"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"બંધ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 330b6f1..555b3d7 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"निचले किनारे से <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"बाएं किनारे से <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"दाएं किनारे से <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"वर्क प्रोफ़ाइल से लिए गए स्क्रीनशॉट, <xliff:g id="APP">%1$s</xliff:g> ऐप्लिकेशन में सेव किए गए हैं"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रिकॉर्डिंग को प्रोसेस किया जा रहा है"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रिकॉर्ड सेशन के लिए जारी सूचना"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"अपने-आप"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"किसी तरह की आवाज़ या वाइब्रेशन न हो"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और बातचीत, सेक्शन में सबसे नीचे दिखती है"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है. <xliff:g id="APP_NAME">%1$s</xliff:g> में होने वाली बातचीत, डिफ़ॉल्ट रूप से बबल के तौर पर दिखती है."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टम को यह तय करने की अनुमति दें कि इस सूचना के मिलने पर आवाज़ हो या वाइब्रेशन हो"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>स्थिति:</b> लेवल बढ़ाकर, डिफ़ॉल्ट के तौर पर सेट किया गया"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>स्थिति:</b> लेवल घटाकर, साइलेंट पर सेट किया गया"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"मध्यम"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"छोटा"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"बड़ा"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"हो गया"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"बदलाव करें"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ज़ूम करने की सुविधा वाली विंडो से जुड़ी सेटिंग"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सुलभता सुविधाएं खोलने के लिए टैप करें. सेटिंग में, इस बटन को बदलें या अपने हिसाब से सेट करें.\n\n"<annotation id="link">"सेटिंग देखें"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> पर, <xliff:g id="SONG_NAME">%1$s</xliff:g> चलाएं"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"पहले जैसा करें"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर मीडिया चलाने के लिए, अपने डिवाइस को उसके पास ले जाएं"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"मीडिया ट्रांसफ़र करने के लिए, <xliff:g id="DEVICENAME">%1$s</xliff:g> के करीब जाएं"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर मीडिया चल रहा है"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"कोई गड़बड़ी हुई. फिर से कोशिश करें."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"लोड हो रहा है"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"टैबलेट"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"काम नहीं कर रहा, ऐप जांचें"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"कंट्रोल नहीं है"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"कंट्रोल मौजूद नहीं है"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"वॉल्यूम"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पीकर और डिसप्ले"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"सुझाए गए डिवाइस"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्ट करने की सुविधा कैसे काम करती है"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करें"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ यह स्क्रीन बंद हो जाएगी"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फ़ोल्ड किया जा सकने वाला डिवाइस अनफ़ोल्ड किया जा रहा है"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फ़ोल्ड किया जा सकने वाला डिवाइस पलटा जा रहा है"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> बैटरी बची है"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"अपने स्टाइलस को चार्ज करें"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलस की बैटरी कम है"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index c15fabf..7e0ba00 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka ili vibracije"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka ili vibracije i prikazuje se pri dnu odjeljka razgovora"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Možda će zvoniti ili vibrirati, ovisno o postavkama telefona"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Možda će zvoniti ili vibrirati, ovisno o postavkama telefona. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačiću prema zadanim postavkama."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Možda će zvoniti ili vibrirati, ovisno o postavkama uređaja"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Možda će zvoniti ili vibrirati, ovisno o postavkama uređaja. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačiću prema zadanim postavkama."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sustav odredi treba li obavijest najaviti zvukom ili vibracijom"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> promaknuta u zadanu"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> prebačena u bešumnu"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Srednja"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Mala"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Velika"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Gotovo"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Uredi"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Postavke prozora povećala"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za otvaranje značajki pristupačnosti. Prilagodite ili zamijenite taj gumb u postavkama.\n\n"<annotation id="link">"Pregledajte postavke"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Približite se radi reprodukcije na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Da biste reproducirali ovdje, približite se uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Nešto nije u redu. Pokušajte ponovo."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Učitavanje"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i zasloni"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Zahtijeva račun s naplatom"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako emitiranje funkcionira"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emitiranje"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u blizini s kompatibilnim Bluetooth uređajima mogu slušati medije koje emitirate"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ovaj će se zaslon isključiti"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rasklopljen sklopivi uređaj"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Okretanje sklopivog uređaja sa svih strana"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je <xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Slaba baterija pisaljke"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nije moguće uspostavljati pozive s ovog profila"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Vaša pravila za poslovne uređaje omogućuju vam upućivanje poziva samo s poslovnog profila"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Prijeđite na poslovni profil"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zatvori"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 39deae7..4c8c988 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alsó rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Bal oldali rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Jobb oldali rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"A munkahelyi képernyőképeket a(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazásba menti a rendszer"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fájlok"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Képernyőrögzítés feldolgozása"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévő értesítés képernyőrögzítési munkamenethez"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatikus"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Nincs hang és rezgés"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nincs hang és rezgés, továbbá lejjebb jelenik meg a beszélgetések szakaszában"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"A telefonbeállítások alapján csöröghet és rezeghet"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"A telefonbeállítások alapján csöröghet és rezeghet. A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásban lévő beszélgetések alapértelmezés szerint buborékban jelennek meg."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"A rendszer határozza meg, hogy ez az értesítés adjon-e ki hangot, illetve rezegjen-e"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Állapot:</b> alapértelmezettre állítva"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Állapot:</b> némára állítva"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> lejátszása innen: <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Visszavonás"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Menjen közelebb, ha itt szeretné lejátszani: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ha szeretné itt lejátszani, helyezkedjen közelebb a következőhöz: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Lejátszás folyamatban a(z) <xliff:g id="DEVICENAME">%1$s</xliff:g> eszközön"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Hiba történt. Próbálkozzon újra."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Betöltés…"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"táblagép"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenőrizze az appot"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nem található"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Nem hozzáférhető vezérlő"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hangerő"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hangfalak és kijelzők"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Javasolt eszközök"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"A közvetítés működése"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Közvetítés"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ A képernyő kikapcsol"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Összehajtható eszköz kihajtása"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Összehajtható eszköz körbeforgatása"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkumulátor töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tegye töltőre az érintőceruzát"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Az érintőceruza töltöttsége alacsony"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index f2e10d4..370ce823 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ներքևի սահմանագիծը՝ <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Ձախ կողմի սահմանագիծը՝ <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Աջ կողմի սահմանագիծը՝ <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Աշխատանքային սքրինշոթները պահվում են «<xliff:g id="APP">%1$s</xliff:g>» հավելվածում"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Ֆայլեր"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Էկրանի տեսագրության մշակում"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Ավտոմատ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Առանց ձայնի կամ թրթռոցի"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Առանց ձայնի և թրթռոցի, հայտնվում է զրույցների ցանկի ներքևում"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Կարող է զնգալ կամ թրթռալ (հեռախոսի կարգավորումներից կախված)"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Կարող է զնգալ կամ թրթռալ (հեռախոսի կարգավորումներից կախված)։ <xliff:g id="APP_NAME">%1$s</xliff:g>-ի զրույցներն ըստ կանխադրման հայտնվում են ամպիկների տեսքով։"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Թող համակարգն ավտոմատ որոշի՝ արդյոք այս ծանուցումը ձայնով, թե թրթռոցով է պետք մատուցել"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Կարգավիճակը․</b> բարձրացվել է և դարձել կանխադրված"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Կարգավիճակը․</b> իջեցվել է և դարձել անձայն"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Միջին"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Փոքր"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Մեծ"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Պատրաստ է"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Փոփոխել"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Խոշորացույցի պատուհանի կարգավորումներ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Հատուկ գործառույթները բացելու համար հպեք։ Անհատականացրեք այս կոճակը կարգավորումներում։\n\n"<annotation id="link">"Կարգավորումներ"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Նվագարկել <xliff:g id="SONG_NAME">%1$s</xliff:g> երգը <xliff:g id="APP_LABEL">%2$s</xliff:g> հավելվածից"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Հետարկել"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ավելի մոտ եկեք՝ <xliff:g id="DEVICENAME">%1$s</xliff:g> սարքում նվագարկելու համար"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Այստեղ նվագարկելու համար մոտեցեք <xliff:g id="DEVICENAME">%1$s</xliff:g> սարքին"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Նվագարկվում է «<xliff:g id="DEVICENAME">%1$s</xliff:g>» սարքում"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Սխալ առաջացավ։ Նորից փորձեք։"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Բեռնվում է"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"պլանշետ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Ակտիվ չէ, ստուգեք հավելվածը"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Չի գտնվել"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Կառավարման տարրը հասանելի չէ"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ձայնի ուժգնություն"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Բարձրախոսներ և էկրաններ"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Առաջարկվող սարքեր"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ինչպես է աշխատում հեռարձակումը"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Հեռարձակում"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Այս էկրանը կանջատվի"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ծալովի սարք՝ բացված վիճակում"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Ծալովի սարք՝ շրջված վիճակում"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ձեր ստիլուսը միացրեք լիցքավորիչի"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Ստիլուսի մարտկոցի լիցքի ցածր մակարդակ"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 93ac4fe..bd1f48b 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Batas bawah <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Batas kiri <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Batas kanan <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Screenshot dengan profil kerja disimpan di aplikasi <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"File"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses perekaman layar"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Otomatis"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Tidak ada suara atau getaran"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tidak ada suara atau getaran dan ditampilkan lebih rendah di bagian percakapan"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Dapat berdering atau bergetar berdasarkan setelan ponsel"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Dapat berdering atau bergetar berdasarkan setelan ponsel. Percakapan dari balon <xliff:g id="APP_NAME">%1$s</xliff:g> secara default."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Biarkan sistem menentukan apakah notifikasi ini akan berbunyi atau bergetar"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Dipromosikan menjadi Default"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Didemosikan menjadi Senyap"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> dari <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Urungkan"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Dekatkan untuk memutar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Untuk memutar di sini, dekatkan ke <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Diputar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Terjadi error. Coba lagi."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Memuat"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrol tidak tersedia"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker & Layar"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Perangkat yang Disarankan"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara kerja siaran"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Siaran"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Layar ini akan dinonaktifkan"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Perangkat foldable sedang dibentangkan"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Perangkat foldable sedang dibalik"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Baterai tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hubungkan stilus ke pengisi daya"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index aa76263..cd924f2 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Neðri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vinstri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Hægri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Vinnuskjámyndir eru vistaðar í forritinu <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Skrár"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Vinnur úr skjáupptöku"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Sjálfvirk"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ekkert hljóð eða titringur"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ekkert hljóð eða titringur og birtist neðar í samtalshluta"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Gæti hringt eða titrað eftir stillingum símans"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Gæti hringt eða titrað eftir stillingum símans. Samtöl á <xliff:g id="APP_NAME">%1$s</xliff:g> birtast sjálfkrafa í blöðru."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Láta kerfið ákvarða hvort hljóð eða titringur fylgir þessari tilkynningu"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Staða:</b> gerð sjálfgefin"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Staða:</b> var gerð þögul"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> í <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Afturkalla"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Færðu nær til að spila í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Farðu nær <xliff:g id="DEVICENAME">%1$s</xliff:g> til að spila hér"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Í spilun í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Eitthvað fór úrskeiðis. Reyndu aftur."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Hleður"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"spjaldtölva"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Fannst ekki"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Stýring er ekki tiltæk"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hljóðstyrkur"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hátalarar og skjáir"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Tillögur að tækjum"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Svona virkar útsending"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Útsending"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Slökkt verður á þessum skjá"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Samanbrjótanlegt tæki opnað"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Samanbrjótanlegu tæki snúið við"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> hleðsla eftir á rafhlöðu"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tengdu pennann við hleðslutæki"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e3cca72..531ac20 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inferiore, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite sinistro, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite destro, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Gli screenshot acquisiti nel profilo di lavoro sono salvati nell\'app <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"File"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaboraz. registraz. schermo"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatico"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Nessun suono o vibrazione"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nessun suono o vibrazione e appare più in basso nella sezione delle conversazioni"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Può suonare o vibrare in base alle impostazioni del telefono"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Può suonare o vibrare in base alle impostazioni del telefono. Le conversazioni di <xliff:g id="APP_NAME">%1$s</xliff:g> appaiono come bolla per impostazione predefinita."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Potrebbero essere attivati lo squillo o la vibrazione in base alle impostazioni del dispositivo"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Potrebbero essere attivati lo squillo o la vibrazione in base alle impostazioni del dispositivo. Conversazioni dalla bolla <xliff:g id="APP_NAME">%1$s</xliff:g> per impostaz. predefinita."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai stabilire al sistema se questa notifica deve emettere suoni o vibrazioni"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stato:</b> promossa a Predefinita"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Stato:</b> retrocessa a Silenziosa"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Medio"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Piccolo"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Grande"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Fine"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Modifica"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Impostazioni della finestra di ingrandimento"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tocca per aprire funzioni di accessibilità. Personalizza o sostituisci il pulsante in Impostazioni.\n\n"<annotation id="link">"Vedi impostazioni"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> da <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annulla"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Avvicinati per riprodurre su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Per riprodurre qui i contenuti, avvicinati a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"In riproduzione su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Si è verificato un errore. Riprova."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Caricamento in corso…"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Controllo non trovato"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Il controllo non è disponibile"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker e display"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivi consigliati"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Occorre un account premium"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Come funziona la trasmissione"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Annuncio"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Le persone vicine a te che hanno dispositivi Bluetooth compatibili possono ascoltare i contenuti multimediali che stai trasmettendo"</string>
@@ -1060,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Questo schermo verrà disattivato"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pieghevole che viene aperto"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pieghevole che viene capovolto"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteria rimanente"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connetti lo stilo a un caricabatterie"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Batteria stilo in esaurimento"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videocamera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Impossibile chiamare da questo profilo"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Le norme di lavoro ti consentono di fare telefonate soltanto dal profilo di lavoro"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Passa a profilo di lavoro"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Chiudi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 5b55241..926e2aa 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"באופן אוטומטי"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ללא צליל או רטט"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ללא צליל או רטט ומופיעה למטה בקטע התראות השיחה"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ייתכן שיופעל צלצול או רטט בהתאם להגדרות הטלפון"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ייתכן שיופעל צלצול או רטט בהתאם להגדרות הטלפון. שיחות מהאפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מופיעות בבועות כברירת מחדל."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"אפשר לתת למערכת לקבוע אם ההתראה הזאת צריכה להיות מלווה בצליל או ברטט"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>הסטטוס:</b> הועלה בדרגה ל\'ברירת מחדל\'"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>הסטטוס:</b> הורד בדרגה ל\'שקט\'"</string>
@@ -812,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"בינוני"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"קטן"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"גדול"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"סיום"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"עריכה"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ההגדרות של חלון ההגדלה"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"מקישים כדי לפתוח את תכונות הנגישות. אפשר להחליף את הלחצן או להתאים אותו אישית בהגדרות.\n\n"<annotation id="link">"הצגת ההגדרות"</annotation></string>
@@ -885,8 +886,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"הפעלת <xliff:g id="SONG_NAME">%1$s</xliff:g> מ-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ביטול"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"צריך להתקרב כדי להפעיל מדיה במכשיר <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"כדי להפעיל במכשיר הזה, יש להתקרב אל <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"פועלת ב-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"משהו השתבש. יש לנסות שוב."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"בטעינה"</string>
@@ -915,6 +915,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"רמקולים ומסכים"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"הצעות למכשירים"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"הסבר על שידורים"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"שידור"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"אנשים בקרבת מקום עם מכשירי Bluetooth תואמים יכולים להאזין למדיה שמשודרת על ידך"</string>
@@ -1056,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ המסך יכבה"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"מכשיר מתקפל עובר למצב לא מקופל"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"מכשיר מתקפל עובר למצב מהופך"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"רמת הטעינה שנותרה בסוללה: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"כדאי לחבר את הסטיילוס למטען"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"הסוללה של הסטיילוס חלשה"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 96f7758..8cd79df 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"下部の境界線 <xliff:g id="PERCENT">%1$d</xliff:g> パーセント"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"左の境界線 <xliff:g id="PERCENT">%1$d</xliff:g> パーセント"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"右の境界線 <xliff:g id="PERCENT">%1$d</xliff:g> パーセント"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"仕事用のスクリーンショットは <xliff:g id="APP">%1$s</xliff:g> アプリに保存されます"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ファイル"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"画面の録画を処理しています"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"着信音もバイブレーションも無効になります"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"着信音もバイブレーションも無効になり会話セクションの下に表示されます"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"スマートフォンの設定を基に着信音またはバイブレーションが有効になります"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"スマートフォンの設定を基に着信音またはバイブレーションが有効になります。デフォルトでは <xliff:g id="APP_NAME">%1$s</xliff:g> からの会話がバブルとして表示されます。"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"デバイスの設定を基に着信音またはバイブレーションが有効になります"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"デバイスの設定を基に着信音またはバイブレーションが有効になります。デフォルトでは <xliff:g id="APP_NAME">%1$s</xliff:g> からの会話がふきだしで表示されます。"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"この通知を音またはバイブレーションで知らせるかどうかの自動判断"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ステータス:</b> ランクがデフォルトに上がりました"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ステータス:</b> ランクがサイレントに下がりました"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"中"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"小"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"大"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"完了"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"編集"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"拡大鏡ウィンドウの設定"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"タップしてユーザー補助機能を開きます。ボタンのカスタマイズや入れ替えを [設定] で行えます。\n\n"<annotation id="link">"設定を表示"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> を <xliff:g id="APP_LABEL">%2$s</xliff:g> で再生"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"元に戻す"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生するにはもっと近づけてください"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"このデバイスで再生するには、<xliff:g id="DEVICENAME">%1$s</xliff:g> にもっと近づけてください"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生しています"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"エラーが発生しました。もう一度お試しください。"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"読み込んでいます"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"タブレット"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"無効: アプリをご確認ください"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"見つかりませんでした"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"コントロールを使用できません"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"スピーカーとディスプレイ"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"デバイスの候補"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"プレミアム アカウントが必要です"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ブロードキャストの仕組み"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ブロードキャスト"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth 対応デバイスを持っている付近のユーザーは、あなたがブロードキャストしているメディアを聴けます"</string>
@@ -1060,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱この画面は OFF になります"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"折りたたみ式デバイスが広げられている"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"折りたたみ式デバイスがひっくり返されている"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"バッテリー残量 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"タッチペンを充電器に接続してください"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"タッチペンのバッテリー残量が少なくなっています"</string>
+ <string name="video_camera" msgid="7654002575156149298">"ビデオカメラ"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"このプロファイルからは通話を発信できません"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"仕事用ポリシーでは、通話の発信を仕事用プロファイルからのみに制限できます"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"仕事用プロファイルに切り替える"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"閉じる"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 5f9b6e1..fe0a8a0 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ავტომატური"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ხმისა და ვიბრაციის გარეშე"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ხმისა და ვიბრაციის გარეშე, ჩნდება მიმოწერების სექციის ქვედა ნაწილში"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით. მიმოწერები <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ბუშტიდან, ნაგულისხმევად."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"დარეკვა ან ვიბრაცია მოწყობილობის პარამეტრების მიხედვით"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"დარეკვა ან ვიბრაცია მოწყობილობის პარამეტრების მიხედვით. მიმოწერები <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ბუშტიდან, ნაგულისხმევად."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"სისტემისთვის ისეთი უფლების მინიჭება, რომ მან განსაზღვროს, ამ შეტყობინებამ ხმოვანი სიგნალი უნდა აამოქმედოს თუ ვიბრაცია"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>სტატუსი:</b> ნაგულისხმევად გარდაქმნილი"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>სტატუსი:</b> „უხმო“ სტატუსზე გადასული"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"საშუალო"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"პატარა"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"დიდი"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"მზადაა"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"რედაქტირება"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"გადიდების ფანჯრის პარამეტრები"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"შეეხეთ მარტივი წვდომის ფუნქციების გასახსნელად. მოარგეთ ან შეცვალეთ ეს ღილაკი პარამეტრებში.\n\n"<annotation id="link">"პარამეტრების ნახვა"</annotation></string>
@@ -883,11 +882,10 @@
<string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"გახსენით <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="APP_LABEL">%3$s</xliff:g>-დან"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g>-დან"</string>
- <string name="media_transfer_undo" msgid="1895606387620728736">"მოქმედების გაუქმება"</string>
+ <string name="media_transfer_undo" msgid="1895606387620728736">"მოქმედ.გაუქმება"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"მიიტანეთ უფრო ახლოს, რომ დაუკრათ <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"მიმდინარეობს დაკვრა <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"აქ სათამაშოდ, მიუახლოვდით <xliff:g id="DEVICENAME">%1$s</xliff:g>-ს"</string>
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"უკრავს <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"რაღაც შეცდომა მოხდა. ცადეთ ხელახლა."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"იტვირთება"</string>
<string name="media_ttt_default_device_type" msgid="4457646436153370169">"ტაბლეტი"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და დისპლეები"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"შემოთავაზებული მოწყობილობები"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"საჭიროა პრემიუმ-ანგარიში"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ტრანსლირების მუშაობის პრინციპი"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ტრანსლაცია"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"თქვენთან ახლოს მყოფ ხალხს თავსებადი Bluetooth მოწყობილობით შეუძლიათ თქვენ მიერ ტრანსლირებული მედიის მოსმენა"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ეს ეკრანი გამოირთვება"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"დასაკეცი მოწყობილობა იხსნება"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"დასაკეცი მოწყობილობა ტრიალებს"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"დარჩენილია ბატარეის <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"დააკავშირეთ თქვენი სტილუსი დამტენს"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"სტილუსის ბატარეა დაცლის პირასაა"</string>
+ <string name="video_camera" msgid="7654002575156149298">"ვიდეოკამერა"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"ამ პროფილიდან დარეკვა ვერ ხერხდება"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"თქვენი სამსახურის წესები საშუალებას გაძლევთ, სატელეფონო ზარები განახორციელოთ მხოლოდ სამსახურის პროფილიდან"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"სამსახურის პროფილზე გადართვა"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"დახურვა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 77c586a..b2274c8 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Төменгі шектік сызық: <xliff:g id="PERCENT">%1$d</xliff:g> пайыз"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Сол жақ шектік сызық: <xliff:g id="PERCENT">%1$d</xliff:g> пайыз"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Оң жақ шектік сызық: <xliff:g id="PERCENT">%1$d</xliff:g> пайыз"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Жұмыс профилінен алынған скриншоттар <xliff:g id="APP">%1$s</xliff:g> қолданбасында сақталады."</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экран жазғыш бейнесін өңдеу"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматты"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Дыбыс не діріл болмайды."</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл болмайды, әңгімелер бөлімінің төмен жағында тұрады."</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефон параметрлеріне байланысты дыбыстық сигнал не діріл болуы мүмкін."</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефон параметрлеріне байланысты дыбыстық сигнал не діріл болуы мүмкін. <xliff:g id="APP_NAME">%1$s</xliff:g> әңгімелері әдепкісінше қалқып шығады."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Хабарландыру дыбысының немесе дірілдің қосылуын жүйе анықтайтын болады"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Күйі:</b> \"Әдепкі\" санатына көтерілген"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Күйі:</b> \"Үнсіз\" санатына төмендетілген"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> қолданбасында \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" әнін ойнату"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Қайтару"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында музыка ойнату үшін оған жақындаңыз."</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Осы жерде ойнату үшін <xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысына жақындаңыз."</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында ойнатылуда."</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Бірдеңе дұрыс болмады. Қайталап көріңіз."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Жүктеліп жатыр"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"планшет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Өшірулі. Қолданба тексеріңіз."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылмады"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Басқару виджеті қолжетімсіз"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Дыбыс деңгейі"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер мен дисплейлер"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ұсынылған құрылғылар"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Тарату қалай жүзеге асады"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Тарату"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Бұл экран өшіріледі."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Бүктемелі құрылғы ашылып жатыр."</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Бүктемелі құрылғы аударылып жатыр."</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Қалған батарея заряды: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусты зарядтағышқа жалғаңыз."</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index a191a14..d33b439 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"បន្ទាត់បែងចែកខាងក្រោម <xliff:g id="PERCENT">%1$d</xliff:g> ភាគរយ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"បន្ទាត់បែងចែកខាងឆ្វេង <xliff:g id="PERCENT">%1$d</xliff:g> ភាគរយ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"បន្ទាត់បែងចែកខាងស្ដាំ <xliff:g id="PERCENT">%1$d</xliff:g> ភាគរយ"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"រូបថតអេក្រង់ក្នុងកម្រងព័ត៌មានការងារត្រូវបានរក្សាទុកនៅក្នុងកម្មវិធី <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ឯកសារ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថតវីដេអូអេក្រង់"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"កំពុងដំណើរការការថតអេក្រង់"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជូនដំណឹងដែលកំពុងដំណើរការសម្រាប់រយៈពេលប្រើការថតសកម្មភាពអេក្រង់"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ស្វ័យប្រវត្តិ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"គ្មានសំឡេង ឬការញ័រទេ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"គ្មានសំឡេងឬការញ័រ និងបង្ហាញទាបជាងនៅក្នុងផ្នែកសន្ទនា"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"អាចរោទ៍ ឬញ័រ ដោយផ្អែកលើការកំណត់ទូរសព្ទ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"អាចរោទ៍ ឬញ័រ ដោយផ្អែកលើការកំណត់ទូរសព្ទ។ ការសន្ទនាពីពពុះ <xliff:g id="APP_NAME">%1$s</xliff:g> តាមលំនាំដើម។"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ឱ្យប្រព័ន្ធកំណត់ថាតើការជូនដំណឹងនេះគួរតែបន្លឺសំឡេង ឬញ័រ"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ស្ថានភាព៖</b> បានដំឡើងទៅលំនាំដើម"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ស្ថានភាព៖</b> បានបញ្ចុះទៅស្ងាត់"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ពី <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ត្រឡប់វិញ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"រំកិលឱ្យកាន់តែជិត ដើម្បីចាក់នៅលើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ដើម្បីចាក់នៅទីនេះ សូមខិតទៅជិត <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"កំពុងចាក់នៅលើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"មានអ្វីមួយខុសប្រក្រតី។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"កំពុងផ្ទុក"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ថេប្លេត"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"អសកម្ម ពិនិត្យមើលកម្មវិធី"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"រកមិនឃើញទេ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"មិនអាចគ្រប់គ្រងបានទេ"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"កម្រិតសំឡេង"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ឧបករណ៍បំពងសំឡេង និងផ្ទាំងអេក្រង់"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ឧបករណ៍ដែលបានណែនាំ"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"របៀបដែលការផ្សាយដំណើរការ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ការផ្សាយ"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ អេក្រង់នេះនឹងបិទ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ឧបករណ៍អាចបត់បានកំពុងត្រូវបានលា"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ឧបករណ៍អាចបត់បានកំពុងត្រូវបានលា"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ថ្មនៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ភ្ជាប់ប៊ិករបស់អ្នកជាមួយឆ្នាំងសាក"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 652a227..e9dc769 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ಕೆಳಗಿನ ಬೌಂಡರಿ ಶೇಕಡಾ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ಎಡಭಾಗದ ಬೌಂಡರಿ ಶೇಕಡಾ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ಬಲಭಾಗದ ಬೌಂಡರಿ ಶೇಕಡಾ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"<xliff:g id="APP">%1$s</xliff:g> ಆ್ಯಪ್ನಲ್ಲಿ ಉಳಿಸಲಾದ ಕೆಲಸದ ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳು"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ಫೈಲ್ಗಳು"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೆಶನ್ಗಾಗಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಅಧಿಸೂಚನೆ"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ಸ್ವಯಂಚಾಲಿತ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ಯಾವುದೇ ಧ್ವನಿ ಅಥವಾ ವೈಬ್ರೇಷನ್ ಆಗುವುದಿಲ್ಲ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ಯಾವುದೇ ಧ್ವನಿ ಅಥವಾ ವೈಬ್ರೇಷನ್ ಆಗುವುದಿಲ್ಲ, ಸಂಭಾಷಣೆ ವಿಭಾಗದ ಕೆಳಭಾಗದಲ್ಲಿ ಗೋಚರಿಸುತ್ತದೆ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಆಧರಿಸಿ ಫೋನ್ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಆಧರಿಸಿ ಫೋನ್ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ. ಡಿಫಾಲ್ಟ್ ಆಗಿ, <xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಬಬಲ್ ಸಂಭಾಷಣೆಗಳು."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಆಧರಿಸಿ ಸಾಧನ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಆಧರಿಸಿ ಫೋನ್ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ. ಡಿಫಾಲ್ಟ್ ಆಗಿ, <xliff:g id="APP_NAME">%1$s</xliff:g> ಬಬಲ್ ಸಂಭಾಷಣೆಗಳು."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ಈ ಅಧಿಸೂಚನೆಯು ಶಬ್ದ ಮಾಡಬೇಕೇ ಅಥವಾ ವೈಬ್ರೇಟ್ ಮಾಡಬೇಕೇ ಎಂಬುದನ್ನು ನಿರ್ಧರಿಸುವ ಅವಕಾಶವನ್ನು ಸಿಸ್ಟಂಗೆ ನೀಡಿ"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ಸ್ಥಿತಿ:</b> ಡೀಫಾಲ್ಟ್ಗೆ ಬಡ್ತಿ ಹೊಂದಿದೆ"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ಸ್ಥಿತಿ:</b> ಸೈಲೆಂಟ್ಗೆ ಕೆಳದರ್ಜೆಗೆ ಇಳಿದಿದೆ"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"ಮಧ್ಯಮ"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"ಚಿಕ್ಕದು"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"ದೊಡ್ಡದು"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"ಮುಗಿದಿದೆ"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ಮ್ಯಾಗ್ನಿಫೈರ್ ವಿಂಡೋ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಈ ಬಟನ್ ಅನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ ಅಥವಾ ಬದಲಾಯಿಸಿ.\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%2$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ರದ್ದುಗೊಳಿಸಿ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಅದರ ಹತ್ತಿರಕ್ಕೆ ಸರಿಯಿರಿ"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ಇಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು, <xliff:g id="DEVICENAME">%1$s</xliff:g> ಸಮೀಪಕ್ಕೆ ಸರಿಯಿರಿ"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತಿದೆ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ಏನೋ ತಪ್ಪಾಗಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ಟ್ಯಾಬ್ಲೆಟ್"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ಕಂಡುಬಂದಿಲ್ಲ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ನಿಯಂತ್ರಣ ಲಭ್ಯವಿಲ್ಲ"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ವಾಲ್ಯೂಮ್"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ಸ್ಪೀಕರ್ಗಳು ಮತ್ತು ಡಿಸ್ಪ್ಲೇಗಳು"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ಸೂಚಿಸಿದ ಸಾಧನಗಳು"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"ಪ್ರೀಮಿಯಂ ಖಾತೆಯ ಅಗತ್ಯವಿದೆ"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ಪ್ರಸಾರವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ಪ್ರಸಾರ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ಹೊಂದಾಣಿಕೆಯಾಗುವ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ಹೊಂದಿರುವ ಸಮೀಪದಲ್ಲಿರುವ ಜನರು ನೀವು ಪ್ರಸಾರ ಮಾಡುತ್ತಿರುವ ಮಾಧ್ಯಮವನ್ನು ಆಲಿಸಬಹುದು"</string>
@@ -1060,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ಈ ಸ್ಕ್ರೀನ್ ಆಫ್ ಆಗುತ್ತದೆ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ಫೋಲ್ಡ್ ಮಾಡಬಹುದಾದ ಸಾಧನವನ್ನು ಅನ್ಫೋಲ್ಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ಫೋಲ್ಡ್ ಮಾಡಬಹುದಾದ ಸಾಧನವನ್ನು ಸುತ್ತಲೂ ತಿರುಗಿಸಲಾಗುತ್ತಿದೆ"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ ಉಳಿದಿದೆ"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ನಿಮ್ಮ ಸ್ಟೈಲಸ್ ಅನ್ನು ಚಾರ್ಜರ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಿದೆ"</string>
+ <string name="video_camera" msgid="7654002575156149298">"ವೀಡಿಯೊ ಕ್ಯಾಮರಾ"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"ಈ ಪ್ರೊಫೈಲ್ನಿಂದ ಕರೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"ನಿಮ್ಮ ಕೆಲಸದ ನೀತಿಯು ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ನಿಂದ ಮಾತ್ರ ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ಗೆ ಬದಲಿಸಿ"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"ಮುಚ್ಚಿರಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 2346774..c9d70a1 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"하단 가장자리 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"왼쪽 가장자리 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"오른쪽 가장자리 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"직장 프로필의 스크린샷은 <xliff:g id="APP">%1$s</xliff:g> 앱에 저장됩니다."</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"파일"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"화면 녹화 처리 중"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"화면 녹화 세션에 관한 지속적인 알림"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"자동"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"소리 또는 진동 없음"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"소리나 진동이 울리지 않으며 대화 섹션 하단에 표시됨"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있음"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있습니다. 기본적으로 <xliff:g id="APP_NAME">%1$s</xliff:g>의 대화는 대화창으로 표시됩니다."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"시스템에서 알림 시 소리 또는 진동을 사용할지 결정하도록 허용합니다."</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>상태:</b> 기본으로 높임"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>상태:</b> 무음으로 낮춤"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"보통"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"작게"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"크게"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"완료"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"수정"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"돋보기 창 설정"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"접근성 기능을 열려면 탭하세요. 설정에서 이 버튼을 맞춤설정하거나 교체할 수 있습니다.\n\n"<annotation id="link">"설정 보기"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>에서 <xliff:g id="SONG_NAME">%1$s</xliff:g> 재생"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"실행취소"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생하려면 기기를 더 가까이로 옮기세요."</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"여기에서 재생하려면 <xliff:g id="DEVICENAME">%1$s</xliff:g>에 더 가까이 이동하세요."</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생 중"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"문제가 발생했습니다. 다시 시도해 주세요."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"로드 중"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"태블릿"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"비활성. 앱을 확인하세요."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"찾을 수 없음"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"컨트롤을 사용할 수 없음"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"볼륨"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"스피커 및 디스플레이"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"추천 기기"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"브로드캐스팅 작동 원리"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"브로드캐스트"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 이 화면이 꺼집니다."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"폴더블 기기를 펼치는 모습"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"폴더블 기기를 뒤집는 모습"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"배터리 <xliff:g id="PERCENTAGE">%s</xliff:g> 남음"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"스타일러스를 충전기에 연결하세요"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"스타일러스 배터리 부족"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index c05fae2..54a073e 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ылдый жагы <xliff:g id="PERCENT">%1$d</xliff:g> пайызга"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Сол жагы <xliff:g id="PERCENT">%1$d</xliff:g> пайызга"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Оң жагы <xliff:g id="PERCENT">%1$d</xliff:g> пайызга"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Жумуш скриншоттору <xliff:g id="APP">%1$s</xliff:g> колдонмосунда сакталат"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экрандан жаздырылып алынган видео иштетилүүдө"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды жаздыруу сеансы боюнча учурдагы билдирме"</string>
@@ -437,7 +435,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Саясаттарды карап көрүү"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"Башкаруу элементтерин көрүү"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"Бул түзмөк <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> уюмуна таандык.\n\nАдминистраторуңуз бул түзмөктөгү жөндөөлөрдү, корпоративдик ресурстарды пайдалануу мүмкүнчүлүгүн берген параметрлерди жана колдонмолорду, түзмөгүңүзгө байланыштуу маалыматтарды (мисалы, түзмөгүңүздүн жайгашкан жери сыяктуу) көзөмөлдөп башкара алат.\n\nТолугураак маалымат алуу үчүн IT администраторуңузга кайрылыңыз."</string>
- <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> бул түзмөк менен байланышкан маалыматты көрүп, колдонмолорду башкарып, анын жөндөөлөрүн өзгөртө алат.\n\nЭгер суроолоруңуз болсо, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> уюмуна кайрылыңыз."</string>
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> бул түзмөк менен байланышкан маалыматты көрүп, колдонмолорду башкарып, анын параметрлерин өзгөртө алат.\n\nЭгер суроолоруңуз болсо, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> уюмуна кайрылыңыз."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"Бул түзмөк уюмуңузга таандык.\n\nАдминистраторуңуз бул түзмөктөгү жөндөөлөрдү, корпоративдик ресурстарды пайдалануу мүмкүнчүлүгүн берген параметрлерди жана колдонмолорду, түзмөгүңүзгө байланыштуу маалыматтарды (мисалы, түзмөгүңүздүн жайгашкан жери сыяктуу) көзөмөлдөп башкара алат.\n\nТолугураак маалымат алуу үчүн IT администраторуңузга кайрылыңыз."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Ишканаңыз бул түзмөккө тастыктоочу борборду орнотту. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ишканаңыз жумуш профилиңизге тастыктоочу борборду орнотту. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string>
@@ -454,7 +452,7 @@
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ишеним агенти кулпусун ачты"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
- <string name="accessibility_volume_settings" msgid="1458961116951564784">"Добуштун жөндөөлөрү"</string>
+ <string name="accessibility_volume_settings" msgid="1458961116951564784">"Добуштун параметрлери"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматтык коштомо жазуулар"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"Коштомо жазуулар кеңеши"</string>
<string name="volume_odi_captions_content_description" msgid="4172765742046013630">"Коштомо жазуулардын үстүнө коюу"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматтык"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Үнү чыкпайт жана дирилдебейт"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Үнү чыкпайт же дирилдебейт жана сүйлөшүүлөр тизмесинин ылдый жагында көрүнөт"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефондун параметрлерине жараша шыңгырап же дирилдеши мүмкүн"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефондун параметрлерине жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы жазышуулар демейки жөндөө боюнча калкып чыкма билдирмелер түрүндө көрүнөт."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Билдирменин үнүн чыгартууну же басууну тутумга тапшырыңыз"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Абалы:</b> Демейкиге өзгөрдү"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Абалы:</b> Үнсүз абалга төмөндөдү"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Орто"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Кичине"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Чоң"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Бүттү"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Түзөтүү"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Чоңойткуч терезесинин параметрлери"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Атайын мүмкүнчүлүктөрдү ачуу үчүн басыңыз. Бул баскычты Жөндөөлөрдөн өзгөртүңүз.\n\n"<annotation id="link">"Жөндөөлөрдү көрүү"</annotation></string>
@@ -847,7 +846,7 @@
<string name="controls_favorite_removed" msgid="5276978408529217272">"Бардык башкаруу элементтери өчүрүлдү"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Өзгөртүүлөр сакталган жок"</string>
<string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Башка колдонмолорду көрүү"</string>
- <string name="controls_favorite_load_error" msgid="5126216176144877419">"Башкаруу элементтери жүктөлгөн жок. <xliff:g id="APP">%s</xliff:g> колдонмосуна өтүп, колдонмонун жөндөөлөрү өзгөрбөгөнүн текшериңиз."</string>
+ <string name="controls_favorite_load_error" msgid="5126216176144877419">"Башкаруу элементтери жүктөлгөн жок. <xliff:g id="APP">%s</xliff:g> колдонмосуна өтүп, колдонмонун параметрлери өзгөрбөгөнүн текшериңиз."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Шайкеш башкаруу элементтери жеткиликсиз"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Башка"</string>
<string name="controls_dialog_title" msgid="2343565267424406202">"Түзмөктү башкаруу элементтерине кошуу"</string>
@@ -887,17 +886,15 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ырын <xliff:g id="APP_LABEL">%2$s</xliff:g> колдонмосунан ойнотуу"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Кайтаруу"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> түзмөгүндө ойнотуу үчүн жакындатыңыз"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ушул жерде ойнотуу үчүн <xliff:g id="DEVICENAME">%1$s</xliff:g> түзмөгүнө жакындаңыз"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> аркылуу ойнотулууда"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Бир жерден ката кетти. Кайра аракет кылыңыз."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Жүктөлүүдө"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"планшет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Жигерсиз. Колдонмону текшериңиз"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылган жок"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Башкара албайсыз"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүн пайдалана албайсыз. Аны <xliff:g id="APPLICATION">%2$s</xliff:g> колдонмосунан башкарууга мүмкүн же мүмкүн эместигин, ошондой эле колдонмонун жөндөөлөрүнүн өзгөрүлбөгөнүн текшериңиз."</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүн пайдалана албайсыз. Аны <xliff:g id="APPLICATION">%2$s</xliff:g> колдонмосунан башкарууга мүмкүн же мүмкүн эместигин, ошондой эле колдонмонун параметрлеринин өзгөрүлбөгөнүн текшериңиз."</string>
<string name="controls_open_app" msgid="483650971094300141">"Колдонмону ачуу"</string>
<string name="controls_error_generic" msgid="352500456918362905">"Абалы жүктөлгөн жок"</string>
<string name="controls_error_failed" msgid="960228639198558525">"Ката, кайталап көрүңүз"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Үндүн катуулугу"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер жана дисплейлер"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Сунушталган түзмөктөр"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Кабарлоо кантип иштейт"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Кабарлоо"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Бул экран өчөт"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ачылып турган бүктөлмө түзмөк"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Оодарылып жаткан бүктөлмө түзмөк"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Батареянын кубаты: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусту кубаттаңыз"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Стилустун батареясы отурайын деп калды"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 7af21e8..fc386d6 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ຂອບເຂດທາງລຸ່ມ <xliff:g id="PERCENT">%1$d</xliff:g> ເປີເຊັນ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ຂອບເຂດທາງຊ້າຍ <xliff:g id="PERCENT">%1$d</xliff:g> ເປີເຊັນ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ຂອບເຂດທາງຂວາ <xliff:g id="PERCENT">%1$d</xliff:g> ເປີເຊັນ"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"ຮູບໜ້າຈໍວຽກຖືກບັນທຶກຢູ່ໃນແອັບ <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ໄຟລ໌"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ກຳລັງປະມວນຜົນການບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ການແຈ້ງເຕືອນສຳລັບເຊດຊັນການບັນທຶກໜ້າຈໍໃດໜຶ່ງ"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ອັດຕະໂນມັດ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນ ແລະ ປາກົດຢູ່ທາງລຸ່ມຂອງພາກສ່ວນການສົນທະນາ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ອາດສົ່ງສຽງ ຫຼື ສັ່ນເຕືອນໂດຍອ້າງອີງຈາກການຕັ້ງຄ່າໂທລະສັບ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ອາດສົ່ງສຽງ ຫຼື ສັ່ນເຕືອນໂດຍອ້າງອີງຈາກການຕັ້ງຄ່າໂທລະສັບ. ການສົນທະນາຈາກ <xliff:g id="APP_NAME">%1$s</xliff:g> ຈະສະແດງເປັນຟອງຕາມຄ່າເລີ່ມຕົ້ນ."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ໃຫ້ລະບົບກຳນົດວ່າການແຈ້ງເຕືອນນິ້ຄວນມີສຽງ ຫຼື ສັ່ນເຕືອນຫຼືບໍ່"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ສະຖານະ:</b> ເລື່ອນລະດັບເປັນຄ່າເລີ່ມຕົ້ນແລ້ວ"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ສະຖານະ:</b> ຫຼຸດລະດັບເປັນປິດສຽງແລ້ວ"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"ປານກາງ"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"ນ້ອຍ"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"ໃຫຍ່"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"ແລ້ວໆ"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"ແກ້ໄຂ"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ການຕັ້ງຄ່າໜ້າຈໍຂະຫຍາຍ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ແຕະເພື່ອເປີດຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ປັບແຕ່ງ ຫຼື ປ່ຽນປຸ່ມນີ້ໃນການຕັ້ງຄ່າ.\n\n"<annotation id="link">"ເບິ່ງການຕັ້ງຄ່າ"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ຫຼິ້ນ <xliff:g id="SONG_NAME">%1$s</xliff:g> ຈາກ <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ຍົກເລີກ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ຍ້າຍໄປໃກ້ຂຶ້ນເພື່ອຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ເພື່ອຫຼິ້ນຢູ່ບ່ອນນີ້, ໃຫ້ເຂົ້າໃກ້ <xliff:g id="DEVICENAME">%1$s</xliff:g> ຫຼາຍຂຶ້ນ"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"ກຳລັງຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ມີບາງຢ່າງຜິດພາດເກີດຂຶ້ນ. ກະລຸນາລອງໃໝ່."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ກຳລັງໂຫຼດ"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ແທັບເລັດ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ບໍ່ພົບ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ບໍ່ສາມາດໃຊ້ການຄວບຄຸມໄດ້"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ລະດັບສຽງ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ລຳໂພງ ແລະ ຈໍສະແດງຜົນ"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ອຸປະກອນທີ່ແນະນຳ"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ການອອກອາກາດເຮັດວຽກແນວໃດ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ອອກອາກາດ"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ໜ້າຈໍນີ້ຈະປິດ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ອຸປະກອນທີ່ພັບໄດ້ກຳລັງກາງອອກ"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ອຸປະກອນທີ່ພັກໄດ້ກຳລັງປີ້ນໄປມາ"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ແບັດເຕີຣີເຫຼືອ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ເຊື່ອມຕໍ່ປາກກາຂອງທ່ານກັບສາຍສາກ"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"ແບັດເຕີຣີປາກກາເຫຼືອໜ້ອຍ"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index cdf134c..753b8b7 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Apatinė riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kairioji riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Dešinioji riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Ekrano kopijos, užfiksuotos naudojantis darbo profiliu, išsaugomos programoje „<xliff:g id="APP">%1$s</xliff:g>“"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Failai"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Apdorojam. ekrano vaizdo įraš."</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano įrašymo sesijos pranešimas"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatinis"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Neskamba ir nevibruoja"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Neskamba, nevibruoja ir rodoma apatinėje pokalbių skilties dalyje"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Gali skambėti arba vibruoti, atsižvelgiant į telefono nustatymus"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Gali skambėti arba vibruoti, atsižvelgiant į telefono nustatymus. Pokalbiai iš „<xliff:g id="APP_NAME">%1$s</xliff:g>“ debesėlio pagal numatytuosius nustatymus."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Gali skambėti arba vibruoti, atsižvelgiant į įrenginio nustatymus"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Gali skambėti arba vibruoti, atsižvelgiant į įrenginio nustatymus. Pokalbiai iš „<xliff:g id="APP_NAME">%1$s</xliff:g>“ debesėlio pagal numatytuosius nustatymus."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nustatykite, kad sistema aptiktų, ar šis pranešimas turi skambėti, ar vibruoti"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Būsena:</b> pakeista į numatytąjį lygį"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Būsena:</b> pakeista į begarsį lygį"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Vidutinis"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Mažas"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Didelis"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Atlikta"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Redaguoti"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Didinimo lango nustatymai"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Palietę atidarykite pritaikymo neįgaliesiems funkcijas. Tinkinkite arba pakeiskite šį mygtuką nustatymuose.\n\n"<annotation id="link">"Žr. nustatymus"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Leisti „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%2$s</xliff:g>“"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anuliuoti"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Prieikite arčiau, kad galėtumėte leisti įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Jei norite leisti čia, eikite arčiau įrenginio „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Leidžiama įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Kažkas ne taip. Bandykite dar kartą."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Įkeliama"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planšetinis kompiuteris"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nerasta"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Valdiklis nepasiekiamas"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Garsumas"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Garsiakalbiai ir ekranai"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Siūlomi įrenginiai"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Reikalinga mokama paskyra"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kaip veikia transliacija"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transliacija"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Netoliese esantys žmonės, turintys suderinamus „Bluetooth“ įrenginius, gali klausyti jūsų transliuojamos medijos"</string>
@@ -1062,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Lankstomasis įrenginys apverčiamas"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Liko akumuliatoriaus įkrovos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Prijunkite rašiklį prie kroviklio"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Senka rašiklio akumuliatorius"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Vaizdo kamera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Negalima skambinti iš šio profilio"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Pagal jūsų darbo politiką galite skambinti telefonu tik iš darbo profilio"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Perjungti į darbo profilį"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Uždaryti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 603b2b6..f2af1c1 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Apakšmala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kreisā mala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Labā mala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Darba profila ekrānuzņēmumi tiek saglabāti lietotnē <xliff:g id="APP">%1$s</xliff:g>."</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Faili"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekrāna ieraksta apstrāde"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Aktīvs paziņojums par ekrāna ierakstīšanas sesiju"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automātiski"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Nav skaņas signāla vai vibrācijas"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas tālāk sarunu sadaļā"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt. Sarunas no lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> pēc noklusējuma tiek parādītas burbulī."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Iestatiet, lai sistēma noteiktu, vai šim paziņojumam būs skaņa vai vibrācija"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Statuss:</b> svarīgums paaugstināts līdz noklusējumam"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Statuss:</b> svarīgums pazemināts, un paziņojums tiks rādīts bez skaņas"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Atskaņojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” no lietotnes <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Atsaukt"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Pārvietojiet savu ierīci tuvāk, lai atskaņotu mūziku ierīcē “<xliff:g id="DEVICENAME">%1$s</xliff:g>”."</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Lai atskaņotu šeit, jums jāatrodas tuvāk šai ierīcei: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Notiek atskaņošana ierīcē <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Radās kļūda. Mēģiniet vēlreiz."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Notiek ielāde"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planšetdators"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Netika atrasta"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Vadīkla nav pieejama"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Skaļums"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Skaļruņi un displeji"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ieteiktās ierīces"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kā darbojas apraide"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Apraide"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Šis ekrāns tiks izslēgts."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Salokāma ierīce tiek atlocīta"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Salokāma ierīce tiek apgriezta"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Atlikušais uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pievienojiet skārienekrāna pildspalvu lādētājam"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index e1280b8..1517c23 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да ѕвони или вибрира во зависност од поставките на телефонот"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да ѕвони или вибрира во зависност од поставките на телефонот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> поставено на „Стандардно“"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус:</b> намалено на „Тивко“"</string>
@@ -812,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Средно"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Мало"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Големо"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Готово"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Изменете"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Поставки за прозорец за лупа"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Допрете за функциите за пристапност. Приспособете или заменете го копчево во „Поставки“.\n\n"<annotation id="link">"Прикажи поставки"</annotation></string>
@@ -885,8 +886,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пуштете <xliff:g id="SONG_NAME">%1$s</xliff:g> на <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Врати"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Приближете се за да пуштите на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"За да пуштате овде, приближете се до <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Пуштено на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Нешто не е во ред. Обидете се повторно."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Се вчитува"</string>
@@ -915,6 +915,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Звучници и екрани"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени уреди"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционира емитувањето"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Емитување"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Луѓето во ваша близина со компатибилни уреди со Bluetooth може да ги слушаат аудиозаписите што ги емитувате"</string>
@@ -1056,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Екранов ќе се исклучи"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Преклопувачки уред се отклопува"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Преклопувачки уред се врти"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Преостаната батерија: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поврзете го пенкалото со полнач"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 88a3514..365e660 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"സ്വയമേവ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല, സംഭാഷണ വിഭാഗത്തിന് താഴെയായി ദൃശ്യമാകും"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ്/വൈബ്രേറ്റ് ചെയ്യും"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ്/വൈബ്രേറ്റ് ചെയ്തേക്കാം. <xliff:g id="APP_NAME">%1$s</xliff:g>-ൽ നിന്നുള്ള സംഭാഷണങ്ങൾ ഡിഫോൾട്ടായി ബബിൾ ചെയ്യുന്നു."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"ഉപകരണ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്യും അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്യും"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ഉപകരണ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്യും അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്യും. <xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിൽ നിന്നുള്ള സംഭാഷണങ്ങൾ ഡിഫോൾട്ടായി ബബിൾ ചെയ്യുന്നു."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ഈ അറിയിപ്പ് വരുമ്പോൾ ശബ്ദിക്കുകയാണോ വൈബ്രേറ്റ് ചെയ്യുകയാണോ വേണ്ടതെന്ന് നിർണ്ണയിക്കാൻ സിസ്റ്റത്തെ അനുവദിക്കുക"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>നില:</b> ഡിഫോൾട്ടാക്കി പ്രമോട്ട് ചെയ്തു"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>നില:</b> നിശബ്ദമാക്കി തരം താഴ്ത്തി"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"ഇടത്തരം"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"ചെറുത്"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"വലുത്"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"പൂർത്തിയായി"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"എഡിറ്റ് ചെയ്യുക"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"മാഗ്നിഫയർ വിൻഡോ ക്രമീകരണം"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ഉപയോഗസഹായി ഫീച്ചർ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ക്രമീകരണത്തിൽ ഈ ബട്ടൺ ഇഷ്ടാനുസൃതമാക്കാം, മാറ്റാം.\n\n"<annotation id="link">"ക്രമീകരണം കാണൂ"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> എന്ന ഗാനം <xliff:g id="APP_LABEL">%2$s</xliff:g> ആപ്പിൽ പ്ലേ ചെയ്യുക"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"പഴയപടിയാക്കുക"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യാൻ അടുത്തേക്ക് നീക്കുക"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ഇവിടെ പ്ലേ ചെയ്യാൻ <xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിനടുത്തേക്ക് നീക്കുക"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുന്നു"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"എന്തോ കുഴപ്പമുണ്ടായി. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ലോഡ് ചെയ്യുന്നു"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"സ്പീക്കറുകളും ഡിസ്പ്ലേകളും"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"നിർദ്ദേശിച്ച ഉപകരണങ്ങൾ"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"പ്രീമിയം അക്കൗണ്ട് ആവശ്യമാണ്"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ബ്രോഡ്കാസ്റ്റ് എങ്ങനെയാണ് പ്രവർത്തിക്കുന്നത്"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ബ്രോഡ്കാസ്റ്റ്"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"അനുയോജ്യമായ Bluetooth ഉപകരണങ്ങളോടെ സമീപമുള്ള ആളുകൾക്ക് നിങ്ങൾ ബ്രോഡ്കാസ്റ്റ് ചെയ്യുന്ന മീഡിയ കേൾക്കാനാകും"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ഈ സ്ക്രീൻ ഓഫാകും"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ഫോൾഡ് ചെയ്യാവുന്ന ഉപകരണം അൺഫോൾഡ് ആകുന്നു"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ഫോൾഡ് ചെയ്യാവുന്ന ഉപകരണം, കറങ്ങുന്ന വിധത്തിൽ ഫ്ലിപ്പ് ആകുന്നു"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ബാറ്ററി ചാർജ് ശേഷിക്കുന്നു"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"നിങ്ങളുടെ സ്റ്റൈലസ് ചാർജറുമായി കണക്റ്റ് ചെയ്യുക"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"സ്റ്റൈലസിന്റെ ബാറ്ററി ചാർജ് കുറവാണ്"</string>
+ <string name="video_camera" msgid="7654002575156149298">"വീഡിയോ ക്യാമറ"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"ഈ പ്രൊഫൈലിൽ നിന്ന് കോൾ ചെയ്യാനാകില്ല"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"ഔദ്യോഗിക പ്രൊഫൈലിൽ നിന്ന് മാത്രം ഫോൺ കോളുകൾ ചെയ്യാനാണ് നിങ്ങളുടെ ഔദ്യോഗിക നയം അനുവദിക്കുന്നത്"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് മാറുക"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"അടയ്ക്കുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 8358164..a6194c9 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Доод талын хязгаар <xliff:g id="PERCENT">%1$d</xliff:g> хувь"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Зүүн талын хязгаар <xliff:g id="PERCENT">%1$d</xliff:g> хувь"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Баруун талын хязгаар <xliff:g id="PERCENT">%1$d</xliff:g> хувь"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Ажлын дэлгэцийн агшнуудыг <xliff:g id="APP">%1$s</xliff:g> апп дээр хадгалсан"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Файлс"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Дэлгэц бичлэг боловсруулж байна"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Дэлгэц бичих горимын үргэлжилж буй мэдэгдэл"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автомат"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Дуу эсвэл чичиргээ байхгүй"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харилцан ярианы хэсгийн доод талд харагдана"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй. <xliff:g id="APP_NAME">%1$s</xliff:g>-н харилцан яриаг өгөгдмөл тохиргооны дагуу бөмбөлөг болгоно."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Энэ мэдэгдэл дуу гаргах эсвэл чичрэх эсэхийг системээр тодорхойлуулаарай"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Төлөв:</b> Өгөгдмөл болгож дэвшүүлсэн"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Төлөв:</b> Чимээгүй болгож зэрэглэлийг нь бууруулсан"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Дунд зэрэг"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Жижиг"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Том"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Болсон"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Засах"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Томруулагчийн цонхны тохиргоо"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Хандалтын онцлогуудыг нээхийн тулд товшино уу. Энэ товчлуурыг Тохиргоо хэсэгт өөрчилж эсвэл солиорой.\n\n"<annotation id="link">"Тохиргоог харах"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g>-г <xliff:g id="APP_LABEL">%2$s</xliff:g> дээр тоглуулах"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Болих"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулахын тулд төхөөрөмжөө ойртуулна уу"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Энд тоглуулахын тулд <xliff:g id="DEVICENAME">%1$s</xliff:g> руу ойртуулна уу"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулж байна"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Алдаа гарлаа. Дахин оролдоно уу."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Ачаалж байна"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Идэвхгүй байна, аппыг шалгана уу"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Олдсонгүй"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Хяналт боломжгүй байна"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Дууны түвшин"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Чанга яригч ба дэлгэц"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Санал болгосон төхөөрөмжүүд"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Нэвтрүүлэлт хэрхэн ажилладаг вэ?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Нэвтрүүлэлт"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Энэ дэлгэц унтарна"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Эвхэгддэг төхөөрөмжийг дэлгэж байна"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Эвхэгддэг төхөөрөмжийг хөнтөрч байна"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> батарей үлдлээ"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Мэдрэгч үзгээ цэнэглэгчтэй холбоорой"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Мэдрэгч үзэгний батарей бага байна"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index caf0b0a..bbcd720 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"खालील सीमेपासून <xliff:g id="PERCENT">%1$d</xliff:g> टक्के"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"डाव्या सीमेपासून <xliff:g id="PERCENT">%1$d</xliff:g> टक्के"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"उजव्या सीमेपासून <xliff:g id="PERCENT">%1$d</xliff:g> टक्के"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"ऑफिससंबंधित स्क्रीनशॉट <xliff:g id="APP">%1$s</xliff:g> अॅपमध्ये सेव्ह केले आहेत"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"फाइल"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रेकॉर्डिंग प्रोसेस सुरू"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रेकॉर्ड सत्रासाठी सुरू असलेली सूचना"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ऑटोमॅटिक"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"आवाज किंवा व्हायब्रेशन नाही"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"आवाज किंवा व्हायब्रेशन नाही आणि संभाषण विभागात सर्वात तळाशी दिसते"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"फोन सेटिंग्जनुसार फोन रिंग किंवा व्हायब्रेट होऊ शकतो"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोन सेटिंग्जच्या आधारावर रिंग किंवा व्हायब्रेट होऊ शकतो. <xliff:g id="APP_NAME">%1$s</xliff:g> मधील संभाषणे बाय डीफॉल्ट बबल होतात."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ही सूचना मिळाल्यावर आवाज व्हावा की व्हायब्रेशन व्हावे ते सिस्टममध्ये नमूद करा"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>स्थिती</b> ही डीफॉल्ट म्हणून प्रमोट केली गेली"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>स्थिती</b> ला सायलंट म्हणून डीमोट केले गेले"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> मध्ये <xliff:g id="SONG_NAME">%1$s</xliff:g> प्ले करा"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"पहिल्यासारखे करा"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले करण्यासाठी जवळ जा"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"येथे प्ले करण्यासाठी, <xliff:g id="DEVICENAME">%1$s</xliff:g> च्या जवळ जा"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले होत आहे"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"काहीतरी चूक झाली. पुन्हा प्रयत्न करा."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"लोड करत आहे"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"टॅबलेट"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय, ॲप तपासा"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"आढळले नाही"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"नियंत्रण उपलब्ध नाही"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"व्हॉल्यूम"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पीकर आणि डिस्प्ले"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"सुचवलेली डिव्हाइस"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्टिंग कसे काम करते"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करा"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ही स्क्रीन बंद होईल"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फोल्ड करता येण्यासारखे डिव्हाइस अनफोल्ड केले जात आहे"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फोल्ड करता येण्यासारखे डिव्हाइस आजूबाजूला फ्लिप केले जात आहे"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> बॅटरी शिल्लक आहे"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"तुमचे स्टायलस चार्जरशी कनेक्ट करा"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"स्टायलस बॅटरी कमी आहे"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 4800990..9b9e72c 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Sempadan bawah <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sempadan kiri <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sempadan kanan <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Tangkapan skrin tugasan disimpan dalam apl <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fail"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Tiada bunyi atau getaran"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tiada bunyi atau getaran dan muncul di sebelah bawah dalam bahagian perbualan"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Mungkin berbunyi atau bergetar berdasarkan tetapan telefon"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Mungkin berbunyi atau bergetar berdasarkan tetapan telefon. Perbualan daripada gelembung <xliff:g id="APP_NAME">%1$s</xliff:g> secara lalai."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Mungkin berbunyi atau bergetar berdasarkan tetapan peranti"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Mungkin berbunyi atau bergetar berdasarkan tetapan peranti. Perbualan daripada gelembung <xliff:g id="APP_NAME">%1$s</xliff:g> secara lalai."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Minta sistem menentukan jika pemberitahuan ini patut menghasilkan bunyi atau getaran"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Dinaikkan Taraf kepada Lalai"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Diturunkan Taraf kepada Senyap"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Sederhana"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Kecil"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Besar"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Selesai"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Edit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Tetapan tetingkap penggadang"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ketik untuk membuka ciri kebolehaksesan. Sesuaikan/gantikan butang ini dalam Tetapan.\n\n"<annotation id="link">"Lihat tetapan"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> daripada <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Buat asal"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Alihkan lebih dekat untuk bermain pada<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Untuk bermain di sini, bergerak lebih dekat kepada <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Dimainkan pada <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Kesilapan telah berlaku. Cuba lagi."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Memuatkan"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kawalan tidak tersedia"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Kelantangan"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Pembesar Suara & Paparan"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Peranti yang Dicadangkan"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Memerlukan akaun premium"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara siaran berfungsi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Siarkan"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang berdekatan anda dengan peranti Bluetooth yang serasi boleh mendengar media yang sedang anda siarkan"</string>
@@ -1060,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Skrin ini akan dimatikan"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Peranti boleh lipat dibuka"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Peranti boleh lipat diterbalikkan"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateri tinggal <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Sambungkan stilus anda kepada pengecas"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Bateri stilus lemah"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Tidak dapat membuat panggilan daripada profil ini"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Dasar kerja anda membenarkan anda membuat panggilan telefon hanya daripada profil kerja"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Tukar kepada profil kerja"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Tutup"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 54eb158..21ebda9 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"အောက်ခြေအနားသတ် <xliff:g id="PERCENT">%1$d</xliff:g> ရာခိုင်နှုန်း"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ဘယ်ဘက်အနားသတ် <xliff:g id="PERCENT">%1$d</xliff:g> ရာခိုင်နှုန်း"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ညာဘက်အနားသတ် <xliff:g id="PERCENT">%1$d</xliff:g> ရာခိုင်နှုန်း"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"အလုပ်နှင့်ဆိုင်သော ဖန်သားပြင်ဓာတ်ပုံများကို <xliff:g id="APP">%1$s</xliff:g> အက်ပ်တွင် သိမ်းသည်"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ဖိုင်များ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"စကရင်ရိုက်ကူးမှု အပြီးသတ်နေသည်"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"အလိုအလျောက်"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"အသံ သို့မဟုတ် တုန်ခါမှုမရှိပါ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"အသံ သို့မဟုတ် တုန်ခါမှုမရှိပါ၊ စကားဝိုင်းကဏ္ဍ၏ အောက်ပိုင်းတွင် မြင်ရသည်"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ဖုန်းဆက်တင်များပေါ် အခြေခံပြီး အသံမြည်နိုင်သည် သို့မဟုတ် တုန်ခါနိုင်သည်"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ဖုန်းဆက်တင်ပေါ် အခြေခံပြီး အသံမြည် (သို့) တုန်ခါနိုင်သည်။ <xliff:g id="APP_NAME">%1$s</xliff:g> မှ စကားဝိုင်းများကို ပူဖောင်းကွက်ဖြင့် အလိုအလျောက်ပြသည်။"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ဤအကြောင်းကြားချက်က အသံ သို့မဟုတ် တုန်ခါမှု ပေးရန် သင့်/မသင့်ကို စနစ်က ဆုံးဖြတ်ပါစေ"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>အခြေအနေ-</b> မူရင်းသို့ ချိန်ညှိထားသည်"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>အခြေအနေ-</b> အသံတိတ်ခြင်းသို့ ပြန်ချိန်ညှိထားသည်"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"အလတ်"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"အသေး"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"အကြီး"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"ပြီးပြီ"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"ပြင်ရန်"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"မှန်ဘီလူးဝင်းဒိုး ဆက်တင်များ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ရန် တို့ပါ။ ဆက်တင်များတွင် ဤခလုတ်ကို စိတ်ကြိုက်ပြင်ပါ (သို့) လဲပါ။\n\n"<annotation id="link">"ဆက်တင်များ ကြည့်ရန်"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%2$s</xliff:g> တွင် ဖွင့်ပါ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"နောက်ပြန်ရန်"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင်ဖွင့်ရန် အနီးသို့ရွှေ့ပါ"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ဤနေရာတွင် ဖွင့်ရန် <xliff:g id="DEVICENAME">%1$s</xliff:g> အနီးသို့ ရွှေ့ပါ"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင် ဖွင့်နေသည်"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"တစ်ခုခုမှားသွားသည်။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ဖွင့်နေသည်"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"တက်ဘလက်"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ရပ်နေသည်၊ အက်ပ်ကို စစ်ဆေးပါ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"မတွေ့ပါ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ထိန်းချုပ်မှု မရနိုင်ပါ"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"အသံအတိုးအကျယ်"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"စပီကာနှင့် ဖန်သားပြင်များ"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"အကြံပြုထားသော စက်ပစ္စည်းများ"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ထုတ်လွှင့်မှုဆောင်ရွက်ပုံ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ထုတ်လွှင့်ခြင်း"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ဤဖန်သားပြင်ကို ပိတ်လိုက်မည်"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ခေါက်နိုင်သောစက်ကို ဖြန့်လိုက်သည်"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ခေါက်နိုင်သောစက်ကို တစ်ဘက်သို့ လှန်လိုက်သည်"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်သေးသည်"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"စတိုင်လပ်စ်ကို အားသွင်းကိရိယာနှင့် ချိတ်ဆက်ခြင်း"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"စတိုင်လပ်စ် ဘက်ထရီ အားနည်းနေသည်"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 70dc961..30fa917 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nedre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Venstre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Høyre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Jobbrelaterte skjermdumper lagres i <xliff:g id="APP">%1$s</xliff:g>-appen"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Filer"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibrering"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ingen lyd eller vibrering, og vises lavere i samtaledelen"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringe eller vibrere basert på telefoninnstillingene"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringe eller vibrere basert på telefoninnstillingene. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> lager bobler som standard."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"La systemet velge om dette varselet skal lage lyd eller vibrere"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Oppgradert til standard"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Nedgradert til lydløst"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Middels"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Liten"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Stor"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Ferdig"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Endre"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Innstillinger for forstørringsvindu"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Trykk for å åpne tilgj.funksjoner. Tilpass eller bytt knappen i Innstillinger.\n\n"<annotation id="link">"Se innstillingene"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> fra <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Angre"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Flytt nærmere for å spille av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"For å spille av her, gå nærmere <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Spilles av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Noe gikk galt. Prøv på nytt."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Laster inn"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"nettbrett"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke funnet"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrollen er utilgjengelig"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Høyttalere og skjermer"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåtte enheter"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Slik fungerer kringkasting"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Kringkasting"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Denne skjermen slås av"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En sammenleggbar enhet blir brettet ut"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En sammenleggbar enhet blir snudd"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri gjenstår"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koble pekepennen til en lader"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 1aa4e43..4cc7c2f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"फेदबाट <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"बायाँ किनाराबाट <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"दायाँ किनाराबाट <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"कार्य प्रोफाइल प्रयोग गरी लिइएका स्क्रिनसटहरू <xliff:g id="APP">%1$s</xliff:g> एपमा सेभ गरिन्छन्"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रिन रेकर्डिङको प्रक्रिया अघि बढाइँदै"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"कुनै स्क्रिन रेकर्ड गर्ने सत्रका लागि चलिरहेको सूचना"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"बज्दैन पनि, भाइब्रेट पनि हुँदैन"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"बज्दैन पनि, भाइब्रेट पनि हुँदैन र वार्तालाप खण्डको तलतिर देखा पर्छ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"फोनको सेटिङका आधारमा घन्टी बज्न वा भाइब्रेट हुन सक्छ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोनको सेटिङका आधारमा घन्टी बज्न वा भाइब्रेट हुन सक्छ। <xliff:g id="APP_NAME">%1$s</xliff:g> का वार्तालापहरू डिफल्ट रूपमा बबलमा देखाइन्छन्।"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टमलाई यो सूचना आउँदा ध्वनि बज्नु पर्छ वा कम्पन हुनु पर्छ भन्ने कुराको निधो गर्न दिनुहोस्"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>स्थिति:</b> सूचनालाई महत्त्वपूर्ण ठानी डिफल्ट मोडमा सेट गरिएको छ"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>स्थिति:</b> सूचनालाई कम महत्त्वपूर्ण ठानी साइलेन्ट मोडमा सेट गरिएको छ"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"मध्यम"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"सानो"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"ठुलो"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"सम्पन्न भयो"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"सम्पादन गर्नुहोस्"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"म्याग्निफायर विन्डोसम्बन्धी सेटिङ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सर्वसुलभता कायम गर्ने सुविधा खोल्न ट्याप गर्नुहोस्। सेटिङमा गई यो बटन कस्टमाइज गर्नुहोस् वा बदल्नुहोस्।\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> बोलको गीत <xliff:g id="APP_LABEL">%2$s</xliff:g> मा बजाउनुहोस्"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"अन्डू गर्नुहोस्"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गर्न आफ्नो डिभाइस नजिकै लैजानुहोस्"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"यो डिभाइसमा प्ले गर्न <xliff:g id="DEVICENAME">%1$s</xliff:g> को अझ नजिक जानुहोस्"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गरिँदै छ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"केही चिज गडबड भयो। फेरि प्रयास गर्नुहोस्।"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"लोड हुँदै छ"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ट्याब्लेट"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"फेला परेन"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"नियन्त्रण उपलब्ध छैन"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"भोल्युम"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पिकर तथा डिस्प्लेहरू"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"सिफारिस गरिएका डिभाइसहरू"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"प्रसारण गर्ने सुविधाले कसरी काम गर्छ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"प्रसारण"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ यो स्क्रिन अफ हुने छ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फोल्ड गर्न मिल्ने डिभाइस अनफोल्ड गरेको देखाइएको एनिमेसन"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फोल्ड गर्न मिल्ने डिभाइस यताउता पल्टाएर देखाइएको एनिमेसन"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ब्याट्री बाँकी छ"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"आफ्नो स्टाइलस चार्जरमा कनेक्ट गर्नुहोस्"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 9aaa56d..8f0d864 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ondergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linkergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Rechtergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Werkscreenshots worden opgeslagen in de <xliff:g id="APP">%1$s</xliff:g>-app"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Bestanden"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen geluid of trilling"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt lager in het gedeelte met gesprekken getoond"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan overgaan of trillen op basis van de telefooninstellingen"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels getoond."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan overgaan of trillen op basis van de apparaatinstellingen"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan overgaan of trillen op basis van de apparaatinstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Het systeem laten bepalen of deze melding geluid moet maken of moet trillen"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> opgeschaald naar Standaard"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> verlaagd naar Stil"</string>
@@ -887,13 +885,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ongedaan maken"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Houd dichter bij <xliff:g id="DEVICENAME">%1$s</xliff:g> om af te spelen"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ga dichter naar <xliff:g id="DEVICENAME">%1$s</xliff:g> toe om hier af te spelen"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Afspelen op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Er is iets misgegaan. Probeer het opnieuw."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Laden"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Niet gevonden"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Beheeroptie niet beschikbaar"</string>
@@ -917,8 +913,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers en schermen"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde apparaten"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Je hebt een premium account nodig"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitzenden werkt"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Uitzending"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mensen bij jou in de buurt met geschikte bluetooth-apparaten kunnen luisteren naar de media die je uitzendt"</string>
@@ -1060,8 +1056,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Dit scherm gaat uit"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Opvouwbaar apparaat wordt uitgevouwen"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Opvouwbaar apparaat wordt gedraaid"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Nog <xliff:g id="PERCENTAGE">%s</xliff:g> batterijlading"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Verbind je stylus met een oplader"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Batterij van stylus bijna leeg"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videocamera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Je kunt niet bellen vanuit dit profiel"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Op basis van je werkbeleid kun je alleen bellen vanuit het werkprofiel"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Overschakelen naar werkprofiel"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Sluiten"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index d7c17a2..b179bc5 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ନିମ୍ନ ସୀମାରେଖା <xliff:g id="PERCENT">%1$d</xliff:g> ଶତକଡ଼ା"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ବାମ ସୀମାରେଖା <xliff:g id="PERCENT">%1$d</xliff:g> ଶତକଡ଼ା"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ଡାହାଣ ସୀମାରେଖା <xliff:g id="PERCENT">%1$d</xliff:g> ଶତକଡ଼ା"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"ୱାର୍କ ସ୍କ୍ରିନସଟଗୁଡ଼ିକୁ <xliff:g id="APP">%1$s</xliff:g> ଆପରେ ସେଭ କରାଯାଇଛି"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ଫାଇଲଗୁଡ଼ିକ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ସ୍କ୍ରିନ ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ଏକ ସ୍କ୍ରିନ୍ ରେକର୍ଡ୍ ସେସନ୍ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ସ୍ୱଚାଳିତ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ ଏବଂ ବାର୍ତ୍ତାଳାପ ବିଭାଗର ନିମ୍ନରେ ଦେଖାଯାଏ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ଫୋନ ସେଟିଂସ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ ହୋଇପାରେ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ। <xliff:g id="APP_NAME">%1$s</xliff:g>ରୁ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଡିଫଲ୍ଟ ଭାବରେ ବବଲ୍ ହୁଏ।"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ଏହି ବିଜ୍ଞପ୍ତି ପ୍ରାପ୍ତ ହେବା ସମୟରେ ସାଉଣ୍ଡ ହେବା ଉଚିତ ନା ଭାଇବ୍ରେସନ୍ ତାହା ସିଷ୍ଟମକୁ ସ୍ଥିର କରିବାକୁ ଦିଅନ୍ତୁ"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ସ୍ଥିତି:</b> ଡିଫଲ୍ଟକୁ ପ୍ରମୋଟ୍ କରାଯାଇଛି"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ସ୍ଥିତି:</b> ନୀରବକୁ ଡିମୋଟ୍ କରାଯାଇଛି"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ରୁ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ପୂର୍ବବତ୍ କରନ୍ତୁ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ପ୍ଲେ କରିବା ପାଇଁ ପାଖକୁ ମୁଭ କରନ୍ତୁ"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ଏଠାରେ ପ୍ଲେ କରିବା ପାଇଁ <xliff:g id="DEVICENAME">%1$s</xliff:g> ପାଖକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ପ୍ଲେ ହେଉଛି"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"କିଛି ତ୍ରୁଟି ହୋଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ଲୋଡ ହେଉଛି"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ଟାବଲେଟ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ମିଳିଲା ନାହିଁ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ନିୟନ୍ତ୍ରଣ ଉପଲବ୍ଧ ନାହିଁ"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ଭଲ୍ୟୁମ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେଗୁଡ଼ିକ"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ପ୍ରସ୍ତାବିତ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ବ୍ରଡକାଷ୍ଟିଂ କିପରି କାମ କରେ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ବ୍ରଡକାଷ୍ଟ"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ଏହି ସ୍କ୍ରିନ ବନ୍ଦ ହୋଇଯିବ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ଫୋଲ୍ଡ କରାଯାଇପାରୁଥିବା ଡିଭାଇସକୁ ଅନଫୋଲ୍ଡ କରାଯାଉଛି"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ଫୋଲ୍ଡ କରାଯାଇପାରୁଥିବା ଡିଭାଇସକୁ ଫ୍ଲିପ କରାଯାଉଛି"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ବେଟେରୀ ଚାର୍ଜ ବାକି ଅଛି"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ଏକ ଚାର୍ଜର ସହ ଆପଣଙ୍କ ଷ୍ଟାଇଲସକୁ କନେକ୍ଟ କରନ୍ତୁ"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"ଷ୍ଟାଇଲସ ବେଟେରୀର ଚାର୍ଜ କମ ଅଛି"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a6fd6eb..d8ffabf 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ਹੇਠਾਂ ਦੀ ਸੀਮਾ <xliff:g id="PERCENT">%1$d</xliff:g> ਫ਼ੀਸਦ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ਖੱਬੇ ਪਾਸੇ ਵਾਲੀ ਸੀਮਾ <xliff:g id="PERCENT">%1$d</xliff:g> ਫ਼ੀਸਦ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ਸੱਜੇ ਪਾਸੇ ਵਾਲੀ ਸੀਮਾ <xliff:g id="PERCENT">%1$d</xliff:g> ਫ਼ੀਸਦ"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"ਕਾਰਜ ਸਕ੍ਰੀਨਸ਼ਾਟਾਂ ਨੂੰ <xliff:g id="APP">%1$s</xliff:g> ਐਪ ਵਿੱਚ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ਫ਼ਾਈਲਾਂ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਜਾਰੀ ਹੈ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ਕਿਸੇ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਸੈਸ਼ਨ ਲਈ ਚੱਲ ਰਹੀ ਸੂਚਨਾ"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ਸਵੈਚਲਿਤ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਗੱਲਬਾਤ ਸੈਕਸ਼ਨ ਵਿੱਚ ਹੇਠਲੇ ਪਾਸੇ ਦਿਸਦੀਆਂ ਹਨ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ। ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਤੌਰ \'ਤੇ <xliff:g id="APP_NAME">%1$s</xliff:g> ਤੋਂ ਗੱਲਾਂਬਾਤਾਂ ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ।"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ਸਿਸਟਮ ਨੂੰ ਨਿਰਧਾਰਤ ਕਰਨ ਦਿਓ ਕਿ ਇਸ ਸੂਚਨਾ ਲਈ ਕੋਈ ਧੁਨੀ ਵਜਾਉਣੀ ਚਾਹੀਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਵਧਾ ਕੇ ਪੂਰਵ-ਨਿਰਧਾਰਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ਤੋਂ <xliff:g id="SONG_NAME">%1$s</xliff:g> ਚਲਾਓ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ਅਣਕੀਤਾ ਕਰੋ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਉਣ ਲਈ ਨੇੜੇ ਲਿਜਾਓ"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ਇੱਥੇ ਚਲਾਉਣ ਲਈ, <xliff:g id="DEVICENAME">%1$s</xliff:g> ਦੇ ਨੇੜੇ ਲਿਆਓ"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ਟੈਬਲੈੱਟ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ਨਹੀਂ ਮਿਲਿਆ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ਕੰਟਰੋਲ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ਅਵਾਜ਼"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ਸਪੀਕਰ ਅਤੇ ਡਿਸਪਲੇਆਂ"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ਸੁਝਾਏ ਗਏ ਡੀਵਾਈਸ"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ਪ੍ਰਸਾਰਨ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ਪ੍ਰਸਾਰਨ"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ਇਹ ਸਕ੍ਰੀਨ ਬੰਦ ਹੋ ਜਾਵੇਗੀ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ਮੋੜਨਯੋਗ ਡੀਵਾਈਸ ਨੂੰ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ਮੋੜਨਯੋਗ ਡੀਵਾਈਸ ਨੂੰ ਆਲੇ-ਦੁਆਲੇ ਫਲਿੱਪ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ ਬਾਕੀ"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ਆਪਣੇ ਸਟਾਈਲਸ ਨੂੰ ਚਾਰਜਰ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 592c334..f25f1a6 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Przycięcie dolnej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Przycięcie lewej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Przycięcie prawej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Służbowe zrzuty ekranu są zapisywane w aplikacji <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Pliki"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Przetwarzam nagrywanie ekranu"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Stałe powiadomienie o sesji rejestrowania zawartości ekranu"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatycznie"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez dźwięku i wibracji"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Brak dźwięku i wibracji, wyświetla się niżej w sekcji rozmów"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Może włączać dzwonek lub wibracje w zależności od ustawień telefonu"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Może włączać dzwonek lub wibracje w zależności od ustawień telefonu. Rozmowy z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> są domyślnie wyświetlane jako dymki."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Pozwól systemowi decydować, czy o powiadomieniu powinien informować dźwięk czy wibracja"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stan:</b> zmieniony na Domyślny"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Stan:</b> zmieniono na Ciche"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> w aplikacji <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Cofnij"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Przysuń się bliżej, aby odtwarzać na urządzeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Aby zagrać tutaj, przysuń bliżej urządzenie <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Odtwarzam na ekranie <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Coś poszło nie tak. Spróbuj ponownie."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Wczytuję"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdź aplikację"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie znaleziono"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Element jest niedostępny"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Głośność"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Głośniki i wyświetlacze"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Proponowane urządzenia"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak działa transmitowanie"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmisja"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Ekran się wyłączy"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Składane urządzenie jest rozkładane"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Składane urządzenie jest obracane"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Pozostało <xliff:g id="PERCENTAGE">%s</xliff:g> baterii"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Podłącz rysik do ładowarki"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Słaba bateria w rysiku"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index c35a7f8..fd5328f 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> promovida a Padrão"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> rebaixada a Silenciosa"</string>
@@ -812,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Médio"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Pequeno"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Grande"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Concluído"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Editar"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configurações da janela de lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
@@ -885,9 +886,8 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime os dispositivos para tocar a mídia neste: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Mídia aberta no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para abrir aqui, aproxime-se do dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Tocando no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Algo deu errado. Tente novamente."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Carregando"</string>
<string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
@@ -915,6 +915,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
@@ -1056,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index f483ef7..5efafc7 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sem som ou vibração"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sem som ou vibração e aparece na parte inferior na secção de conversas."</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode tocar ou vibrar com base nas definições do telemóvel."</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode tocar ou vibrar com base nas definições do telemóvel. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Pode tocar ou vibrar com base nas definições do dispositivo"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pode tocar ou vibrar com base nas definições do dispositivo. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se esta notificação deve emitir um som ou uma vibração"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Estado:</b> promovida para Predefinida"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Estado:</b> despromovida para Silenciosa"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Médio"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Pequeno"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Grande"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Concluir"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Editar"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Definições da janela da lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir funcionalidades de acessibilidade. Personal. ou substitua botão em Defin.\n\n"<annotation id="link">"Ver defin."</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anular"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime-se para reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproduzir aqui, aproxime-se do <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"A reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Algo correu mal. Tente novamente."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"A carregar"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altifalantes e ecrãs"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requer uma conta premium"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmissão"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas de si com dispositivos Bluetooth compatíveis podem ouvir o conteúdo multimédia que está a transmitir"</string>
@@ -1058,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável a ser virado ao contrário"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de bateria restante"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ligue a caneta stylus a um carregador"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da caneta stylus fraca"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Câmara de vídeo"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Não é possível ligar a partir deste perfil"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"A sua Política de Trabalho só lhe permite fazer chamadas telefónicas a partir do perfil de trabalho"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Mudar para perfil de trabalho"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Fechar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index c35a7f8..fd5328f 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> promovida a Padrão"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> rebaixada a Silenciosa"</string>
@@ -812,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Médio"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Pequeno"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Grande"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Concluído"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Editar"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configurações da janela de lupa"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
@@ -885,9 +886,8 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime os dispositivos para tocar a mídia neste: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Mídia aberta no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para abrir aqui, aproxime-se do dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Tocando no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Algo deu errado. Tente novamente."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Carregando"</string>
<string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
@@ -915,6 +915,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
@@ -1056,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 9196f47..c23098a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Marginea de jos la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Marginea stângă la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Marginea dreaptă la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Capturile de ecran pentru serviciu sunt salvate în aplicația <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fișiere"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Fără sunet sau vibrații"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Fără sunet sau vibrații și apare în partea de jos a secțiunii de conversație"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Poate să sune sau să vibreze, în funcție de setările telefonului"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Poate să sune sau să vibreze, în funcție de setările telefonului. Conversațiile din balonul <xliff:g id="APP_NAME">%1$s</xliff:g> în mod prestabilit."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Solicită-i sistemului să stabilească dacă această notificare e sonoră sau cu vibrații."</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stare:</b> promovată la prestabilită"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Stare:</b> setată ca Silențioasă"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Mediu"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Mic"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Mare"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Gata"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Editează"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Setările ferestrei de mărire"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atinge ca să deschizi funcțiile de accesibilitate. Personalizează sau înlocuiește butonul în setări.\n\n"<annotation id="link">"Vezi setările"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anulează"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Apropie-te pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pentru a reda conținutul aici, apropie-te de <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Se redă pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"A apărut o eroare. Încearcă din nou."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Se încarcă"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tabletă"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verifică aplicația"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Comanda este indisponibilă"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și afișaje"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispozitive sugerate"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cum funcționează transmisia"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmite"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Acest ecran se va dezactiva"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispozitiv pliabil care este desfăcut"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispozitiv pliabil care este întors"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterie rămasă"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conectează-ți creionul la un încărcător"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 804f9af..0f1b895 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Граница снизу: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Граница слева: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Граница справа: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Скриншоты сохраняются в приложении \"<xliff:g id="APP">%1$s</xliff:g>\" в рабочем профиле"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Файлы"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обработка записи с экрана…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущее уведомление для записи видео с экрана"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматически"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звука и вибрации"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звука или вибрации, появляется в нижней части списка разговоров"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Звонок или вибрация в зависимости от настроек телефона"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Звонок или вибрация в зависимости от настроек телефона. Разговоры из приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" по умолчанию появляются в виде всплывающего чата"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Звонок или вибрация в зависимости от настроек устройства"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Звонок или вибрация в зависимости от настроек устройства. Разговоры из приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" по умолчанию появляются в виде всплывающего чата."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Система будет сама определять, включать ли звуковой сигнал или вибрацию для уведомления"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> повышено до уровня \"По умолчанию\""</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус:</b> понижено до уровня \"Без звука\""</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Средняя"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Маленькая"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Большая"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"ОК"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Изменить"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Настройка окна лупы"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Нажмите, чтобы открыть спец. возможности. Настройте или замените эту кнопку в настройках.\n\n"<annotation id="link">"Настройки"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Воспроизвести медиафайл \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" из приложения \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Отменить"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Чтобы начать трансляцию на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", подойдите к нему ближе."</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Чтобы начать воспроизведение здесь, подойдите ближе к устройству (<xliff:g id="DEVICENAME">%1$s</xliff:g>)."</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Воспроизводится на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\"."</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Произошла ошибка. Повторите попытку."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Загрузка…"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"планшет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Нет ответа. Проверьте приложение."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не найдено."</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Управление недоступно"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Громкость"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки и дисплеи"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Рекомендуемые устройства"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Требуется премиум-аккаунт"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работают трансляции"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Трансляция"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Находящиеся рядом с вами люди с совместимыми устройствами Bluetooth могут слушать медиафайлы, которые вы транслируете."</string>
@@ -1060,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Этот экран отключится"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Складное устройство в разложенном виде"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Перевернутое складное устройство"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Уровень заряда батареи: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поставьте стилус на зарядку."</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Низкий заряд батареи стилуса"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Невозможно совершить звонок из этого профиля"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Согласно правилам вашей организации вы можете совершать телефонные звонки только из рабочего профиля."</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Перейти в рабочий профиль"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Закрыть"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 518d184..9ad90af 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"පහළ සීමාව සියයට <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"වම් සීමාව සියයට <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"දකුණු සීමාව සියයට <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"වැඩ තිර රූ <xliff:g id="APP">%1$s</xliff:g> යෙදුම තුළ සුරැකෙයි"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ගොනු"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"තිර පටිගත කිරීම සකසමින්"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර පටිගත කිරීමේ සැසියක් සඳහා කෙරෙන දැනුම් දීම"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ස්වයංක්රිය"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"හඬක් හෝ කම්පනයක් නැත"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"හඬක් හෝ කම්පනයක් නැති අතර සංවාද කොටසේ පහළම දිස් වේ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"දුරකථන සැකසීම් මත පදනම්ව නාද කිරීමට හෝ කම්පනය කිරීමට හැකිය"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"දුරකථන සැකසීම් මත පදනම්ව නාද කිරීමට හෝ කම්පනය කිරීමට හැකිය. <xliff:g id="APP_NAME">%1$s</xliff:g> වෙතින් සංවාද පෙරනිමියෙන් බුබුළු දමයි"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"උපාංග සැකසීම් මත පදනම්ව නාද වීමට හෝ කම්පනය විය හැක"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"උපාංග සැකසීම් මත පදනම්ව නාද වීමට හෝ කම්පනය විය හැක. <xliff:g id="APP_NAME">%1$s</xliff:g> වෙතින් සංවාද පෙරනිමියෙන් බුබුළු දමයි."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"මෙම දැනුම් දීම ශබ්දයක් හෝ කම්පනයක් ඇති කළ යුතු ද යන්න පද්ධතිය මගින් තීරණය කර තිබේද"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>තත්ත්වය:</b> පෙරනිමි වෙත ප්රවර්ධනය කරන ලදි"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>තත්ත්වය:</b> නිශ්ශබ්ද වෙත පහත දමන ලදි"</string>
@@ -887,13 +885,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> වෙතින් වාදනය කරන්න"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"පසුගමනය කරන්න"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කිරීමට වඩාත් ළං වන්න"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"මෙහි වාදනය කිරීම සඳහා, <xliff:g id="DEVICENAME">%1$s</xliff:g> වෙත සමීප වන්න"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කරමින්"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"යම් දෙයක් වැරදිණි. නැවත උත්සාහ කරන්න."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"පූරණය වේ"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ටැබ්ලටය"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"අක්රියයි, යෙදුම පරීක්ෂා කරන්න"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"හමු නොවිණි"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"පාලනය ලබා ගත නොහැකිය"</string>
@@ -917,8 +913,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"හඬ පරිමාව"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ස්පීකර් සහ සංදර්ශක"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"යෝජිත උපාංග"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"පාරිතෝෂික ගිණුමක් අවශ්යයි"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"විකාශනය ක්රියා කරන ආකාරය"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"විකාශනය"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ගැළපෙන බ්ලූටූත් උපාංග සහිත ඔබ අවට සිටින පුද්ගලයින්ට ඔබ විකාශනය කරන මාධ්යයට සවන් දිය හැකිය"</string>
@@ -1060,8 +1056,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ මෙම තිරය ක්රියා විරහිත වනු ඇත"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"දිග හැරෙමින් පවතින නැමිය හැකි උපාංගය"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"වටා පෙරළෙමින් තිබෙන නැමිය හැකි උපාංගය"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> බැටරිය ඉතිරිව ඇත"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ඔබේ පන්හිඳ චාජරයකට සම්බන්ධ කරන්න"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"පන්හිඳ බැටරිය අඩුයි"</string>
+ <string name="video_camera" msgid="7654002575156149298">"වීඩියෝ කැමරාව"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"මෙම පැතිකඩෙන් ඇමතීමට නොහැක"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"ඔබේ වැඩ ප්රතිපත්තිය ඔබට කාර්යාල පැතිකඩෙන් පමණක් දුරකථන ඇමතුම් ලබා ගැනීමට ඉඩ සලසයි"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"කාර්යාල පැතිකඩ වෙත මාරු වන්න"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"වසන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 7dc85b8..a646e5b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> %% dolnej hranice"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> %% ľavej hranice"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> %% pravej hranice"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Pracovné snímky obrazovky sú uložené v aplikácii <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Spracúva sa záznam obrazovky"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Zobrazuje sa upozornenie týkajúce sa relácie záznamu obrazovky"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Žiadny zvuk ani vibrácie"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Žiadny zvuk ani vibrácie a zobrazuje sa nižšie v sekcii konverzácií"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Môže zvoniť či vibrovať podľa nastavení telefónu"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Môže zvoniť alebo vibrovať podľa nastavení telefónu. Predvolene sa zobrazia konverzácie z bubliny <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Môže zvoniť či vibrovať podľa nastavení v zariadení"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Môže zvoniť alebo vibrovať podľa nastavení v zariadení. Predvolene sa zobrazia konverzácie z bubliny aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechajte systém určiť, či má toto upozornenie vydávať zvuk alebo vibrovať"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stav:</b> Preradené vyššie do kategórie Predvolené"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Preradené nižšie do kategórie Tiché"</string>
@@ -887,13 +885,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Prehrať skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Späť"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ak chcete prehrávať v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>, priblížte sa k nemu"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ak tu chcete prehrávať obsah, priblížte zariadenie k zariadeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Prehráva sa v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Niečo sa pokazilo. Skúste to znova."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Načítava sa"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenájdené"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Ovládač nie je k dispozícii"</string>
@@ -917,8 +913,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hlasitosť"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a obrazovky"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Navrhované zariadenia"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"Vyžaduje prémiový účet"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ako vysielanie funguje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Vysielanie"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ľudia v okolí s kompatibilnými zariadeniami s rozhraním Bluetooth si môžu vypočuť médiá, ktoré vysielate"</string>
@@ -1060,8 +1056,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Táto obrazovka sa vypne"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rozloženie skladacieho zariadenia"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Prevrátenie skladacieho zariadenia"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g> batérie"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pripojte dotykové pero k nabíjačke"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stav batérie dotykového pera je nízky"</string>
+ <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"Z tohto profilu nemôžete volať"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"Pracovné pravidlá vám umožňujú telefonovať iba v pracovnom profile"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"Prepnúť na pracovný profil"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zavrieť"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 9d3c287..48c4a66 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Meja spodaj <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Meja levo <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Meja desno <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Posnetki zaslona v delovnem profilu so shranjeni v aplikaciji <xliff:g id="APP">%1$s</xliff:g>."</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Datoteke"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obdelava videoposnetka zaslona"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obveščanje o seji snemanja zaslona"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Samodejno"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Brez zvočnega opozarjanja ali vibriranja."</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Brez zvočnega opozarjanja ali vibriranja, prikaz nižje v razdelku Pogovor."</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev telefona."</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev telefona. Pogovori v aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> so privzeto prikazani v oblačkih."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Naj sistem določi, ali ob prejemu tega obvestila naprava predvaja zvok ali zavibrira"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stanje:</b> Uvrščeno med privzeta obvestila"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Stanje:</b> Uvrščeno med obvestila brez zvoka"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Srednja"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Majhna"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Velika"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Končano"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Uredi"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavitve okna povečevalnika"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dotaknite se za funkcije za ljudi s posebnimi potrebami. Ta gumb lahko prilagodite ali zamenjate v nastavitvah.\n\n"<annotation id="link">"Ogled nastavitev"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Razveljavi"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Za predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g> bolj približajte telefon"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Če želite predvajati tukaj, se približajte napravi <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Prišlo je do napake. Poskusite znova."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Nalaganje"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablični računalnik"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ni mogoče najti"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrolnik ni na voljo"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Glasnost"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvočniki in zasloni"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predlagane naprave"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako deluje oddajanje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Oddajanje"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ta zaslon se bo izklopil."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Razpiranje zložljive naprave"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Obračanje zložljive naprave"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostanek energije baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisalo s polnilnikom."</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 14438ef..0713019 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Kufiri i poshtëm <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kufiri i majtë <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Kufiri i djathtë <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Pamjet e ekranit të punës janë ruajtur në aplikacionin \"<xliff:g id="APP">%1$s</xliff:g>\""</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Skedarë"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Njoftim i vazhdueshëm për një seancë regjistrimi të ekranit"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatike"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Asnjë tingull ose dridhje"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Asnjë tingull ose dridhje dhe shfaqet më poshtë në seksionin e bisedave"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të telefonit"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të telefonit. Si parazgjedhje, bisedat nga <xliff:g id="APP_NAME">%1$s</xliff:g> shfaqen si flluska."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Kërkoji sistemit të përcaktojë nëse ky njoftim duhet të lëshojë tingull apo dridhje"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Statusi:</b> Promovuar si parazgjedhje"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Statusi:</b> Ulur në nivel si në heshtje"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Zhbëj"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Afrohu për të luajtur në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Për ta luajtur këtu, afrohu më shumë te <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Po luhet në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Ndodhi një gabim. Provo përsëri."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Po ngarkohet"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nuk u gjet"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrolli është i padisponueshëm"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumi"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altoparlantët dhe ekranet"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Pajisjet e sugjeruara"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Si funksionon transmetimi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmetimi"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ky ekran do të fiket"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Pajisja e palosshme duke u hapur"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Pajisja e palosshme duke u rrotulluar"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Përqindja e mbetur e baterisë: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Lidhe stilolapsin me një karikues"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e724213..864b861 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Доња ивица <xliff:g id="PERCENT">%1$d</xliff:g> посто"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Лева ивица <xliff:g id="PERCENT">%1$d</xliff:g> посто"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Десна ивица <xliff:g id="PERCENT">%1$d</xliff:g> посто"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Снимци екрана за посао се чувају у апликацији <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Фајлови"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обрађујемо видео снимка екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Обавештење о сесији снимања екрана је активно"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Аутоматска"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звука и вибрирања"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звука и вибрирања и приказује се у наставку одељка за конверзације"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да звони или вибрира у зависности од подешавања телефона"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да звони или вибрира у зависности од подешавања телефона. Конверзације из апликације <xliff:g id="APP_NAME">%1$s</xliff:g> се подразумевано приказују у облачићима."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Нека систем утврди да ли ово обавештење треба да емитује звук или да вибрира"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> Унапређено у Подразумевано"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус:</b> Деградирано у Нечујно"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Средње"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Мало"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Велико"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Готово"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Измени"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Подешавања прозора за увећање"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Додирните за функције приступачности. Прилагодите или замените ово дугме у Подешавањима.\n\n"<annotation id="link">"Подешавања"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пустите <xliff:g id="SONG_NAME">%1$s</xliff:g> из апликације <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Опозови"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Приближите да бисте пуштали музику на: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Да бисте пуштали садржај овде, приближите уређају <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Пушта се на уређају <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Дошло је до грешке. Пробајте поново."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Учитава се"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно. Видите апликацију"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Није пронађено"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Контрола није доступна"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Звук"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Звучници и екрани"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени уређаји"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционише емитовање"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Емитовање"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Овај екран ће се искључити"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Уређај на преклоп се отвара"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Уређај на преклоп се обрће"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Преостало је још<xliff:g id="PERCENTAGE">%s</xliff:g> батерије"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Повежите писаљку са пуњачем"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Низак ниво батерије писаљке"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index eeee083..24c939c 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatiskt"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Inga ljud eller vibrationer"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Inga ljud eller vibrationer och visas längre ned bland konversationerna"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan ringa eller vibrera beroende på inställningarna på telefonen"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringa eller vibrera beroende på inställningarna på telefonen. Konversationer från <xliff:g id="APP_NAME">%1$s</xliff:g> visas i bubblor som standard."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Låt systemet avgöra om den här aviseringen ska låta eller vibrera"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Ändrad till Standard"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Ändrad till Tyst"</string>
@@ -885,8 +887,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> från <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ångra"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Flytta närmare för att spela upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Kom närmare <xliff:g id="DEVICENAME">%1$s</xliff:g> om du vill spela upp här"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Spelas upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Något gick fel. Försök igen."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Läser in"</string>
@@ -915,6 +916,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Högtalare och skärmar"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Förslag på enheter"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Så fungerar utsändning"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Utsändning"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i närheten med kompatibla Bluetooth-enheter kan lyssna på medieinnehåll som du sänder ut"</string>
@@ -1056,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Den här skärmen inaktiveras"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En vikbar enhet viks upp"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En vikbar enhet vänds"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> av batteriet återstår"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Anslut e-pennan till en laddare"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"E-pennans batterinivå är låg"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8e1067c..1c1d71e 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Mpaka wa sehemu ya chini wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Mpaka wa sehemu ya kushoto wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Mpaka wa sehemu ya kulia wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Picha ya skrini ya kazi huhifadhiwa kwenye programu ya <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Faili"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Otomatiki"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Hakuna sauti wala mtetemo"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Hakuna sauti wala mtetemo na huonekana upande wa chini katika sehemu ya mazungumzo"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Huenda ikalia au kutetema kulingana na mipangilio ya simu"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Huenda ikalia au kutetema kulingana na mipangilio ya simu. Mazungumzo kutoka kiputo cha <xliff:g id="APP_NAME">%1$s</xliff:g> kwa chaguomsingi."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ruhusu mfumo ubainishe iwapo arifa hii inapaswa kutoa sauti au mtetemo"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Hali:</b> Imepandishwa Hadhi Kuwa Chaguomsingi"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Imeshushwa Hadhi Kuwa Kimya"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> katika <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Tendua"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Sogeza karibu ili ucheze kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ili ucheze maudhui kwenye kifaa hiki, sogeza karibu na <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Inacheza kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Hitilafu fulani imetokea. Jaribu tena."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Inapakia"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"kompyuta kibao"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hakipatikani"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kidhibiti hakipatikani"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Sauti"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Spika na Skrini"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vifaa Vilivyopendekezwa"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jinsi utangazaji unavyofanya kazi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Tangaza"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Skrini hii itajizima"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Kifaa kinachokunjwa kikikunjuliwa"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Kifaa kinachokunjwa kikigeuzwa"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Chaji ya betri imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 2276e75..07f90ba 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"கீழ் எல்லை <xliff:g id="PERCENT">%1$d</xliff:g> சதவீதம்"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"இடது எல்லை <xliff:g id="PERCENT">%1$d</xliff:g> சதவீதம்"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"வலது எல்லை <xliff:g id="PERCENT">%1$d</xliff:g> சதவீதம்"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"பணிக் கணக்கு ஸ்கிரீன்ஷாட்டுகள் <xliff:g id="APP">%1$s</xliff:g> ஆப்ஸில் சேமிக்கப்படும்"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ஸ்க்ரீன் ரெக்கார்டிங் செயலாக்கப்படுகிறது"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"திரை ரெக்கார்டிங் அமர்விற்கான தொடர் அறிவிப்பு"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"தானியங்கு"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ஒலி / அதிர்வு இல்லை"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ஒலி / அதிர்வு இல்லாமல் உரையாடல் பிரிவின் கீழ்ப் பகுதியில் தோன்றும்"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"மொபைல் அமைப்புகளின் அடிப்படையில் ஒலிக்கலாம்/அதிரலாம்"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"மொபைல் அமைப்புகளின் அடிப்படையில் ஒலிக்கவோ அதிரவோ செய்யும். <xliff:g id="APP_NAME">%1$s</xliff:g> இலிருந்து வரும் உரையாடல்கள் இயல்பாகவே குமிழாகத் தோன்றும்."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"இந்த அறிவிப்பு ஒலி எழுப்ப வேண்டுமா அதிர வேண்டுமா என்பதை சிஸ்டம் தீர்மானிக்கும்"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>நிலை:</b> இயல்புநிலைக்கு உயர்த்தி அமைக்கப்பட்டது"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>நிலை:</b> சைலன்ட் நிலைக்குக் குறைத்து அமைக்கப்பட்டது"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> பாடலை <xliff:g id="APP_LABEL">%2$s</xliff:g> ஆப்ஸில் பிளேசெய்"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"செயல்தவிர்"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் இயக்க உங்கள் சாதனத்தை அருகில் எடுத்துச் செல்லுங்கள்"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"இங்கு பிளே செய்ய உங்கள் சாதனத்தை <xliff:g id="DEVICENAME">%1$s</xliff:g>அருகில் நகர்த்துங்கள்"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் பிளே ஆகிறது"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ஏதோ தவறாகிவிட்டது. மீண்டும் முயலவும்."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"ஏற்றுகிறது"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"டேப்லெட்"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"செயலில் இல்லை , சரிபார்க்கவும்"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"இல்லை"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"கட்டுப்பாடு இல்லை"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ஒலியளவு"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ஸ்பீக்கர்கள் & டிஸ்ப்ளேக்கள்"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"பரிந்துரைக்கப்படும் சாதனங்கள்"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"பிராட்காஸ்ட் எவ்வாறு செயல்படுகிறது?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"பிராட்காஸ்ட்"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ இந்தத் திரை ஆஃப் ஆகிவிடும்"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"மடக்கத்தக்க சாதனம் திறக்கப்படுகிறது"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"மடக்கத்தக்க சாதனம் ஃபிளிப் செய்யப்பட்டு திருப்பப்படுகிறது"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> பேட்டரி மீதமுள்ளது"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"உங்கள் ஸ்டைலஸைச் சார்ஜருடன் இணையுங்கள்"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"ஸ்டைலஸின் பேட்டரி குறைவாக உள்ளது"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index a749277..824fd11 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ఆటోమేటిక్"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"శబ్దం లేదా వైబ్రేషన్లు ఏవీ లేవు"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"శబ్దం లేదా వైబ్రేషన్ లేదు, సంభాషణ విభాగం దిగువన కనిపిస్తుంది"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ఫోన్ సెట్టింగ్ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ఫోన్ సెట్టింగ్ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు. <xliff:g id="APP_NAME">%1$s</xliff:g> నుండి సంభాషణలు ఆటోమేటిక్గా బబుల్గా కనిపిస్తాయి."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"పరికర సెట్టింగ్ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"పరికర సెట్టింగ్ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు. <xliff:g id="APP_NAME">%1$s</xliff:g> నుండి సంభాషణలు ఆటోమేటిక్గా బబుల్లో కనిపిస్తాయి."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ఈ నోటిఫికేషన్ వచ్చినప్పుడు శబ్దం చేయాలా లేదా వైబ్రేట్ చేయాలా అనేది నిర్ణయించడానికి సిస్టమ్కు అనుమతి ఇవ్వండి"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>స్టేటస్:</b> ఆటోమేటిక్ సెట్టింగ్కు ప్రోమోట్ చేయబడింది"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>స్టేటస్:</b> నిశ్శబ్దం స్థాయికి తగ్గించబడింది"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"మధ్యస్థం"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"చిన్నది"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"పెద్దది"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"పూర్తయింది"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"ఎడిట్ చేయండి"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"మాగ్నిఫయర్ విండో సెట్టింగ్లు"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"యాక్సెసిబిలిటీ ఫీచర్లను తెరవడానికి ట్యాప్ చేయండి. సెట్టింగ్లలో ఈ బటన్ను అనుకూలంగా మార్చండి లేదా రీప్లేస్ చేయండి.\n\n"<annotation id="link">"వీక్షణ సెట్టింగ్లు"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> నుండి <xliff:g id="SONG_NAME">%1$s</xliff:g>ను ప్లే చేయండి"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"చర్య రద్దు"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>లో ప్లే చేయడానికి దగ్గరగా వెళ్లండి"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ఇక్కడ ఆడటానికి, <xliff:g id="DEVICENAME">%1$s</xliff:g>కు దగ్గరగా వెళ్లండి"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>లో ప్లే అవుతోంది"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ఏదో తప్పు జరిగింది. మళ్లీ ట్రై చేయండి."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"లోడ్ అవుతోంది"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"స్పీకర్లు & డిస్ప్లేలు"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"సూచించబడిన పరికరాలు"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"ప్రీమియం ఖాతా అవసరం"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ప్రసారం కావడం అనేది ఎలా పని చేస్తుంది"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ప్రసారం"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"మీకు సమీపంలో ఉన్న వ్యక్తులు అనుకూలత ఉన్న బ్లూటూత్ పరికరాలతో మీరు ప్రసారం చేస్తున్న మీడియాను వినగలరు"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ఈ స్క్రీన్ ఆఫ్ అవుతుంది"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"మడవగల పరికరం విప్పబడుతోంది"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"మడవగల పరికరం చుట్టూ తిప్పబడుతోంది"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> బ్యాటరీ మిగిలి ఉంది"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"మీ స్టైలస్ను ఛార్జర్కి కనెక్ట్ చేయండి"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string>
+ <string name="video_camera" msgid="7654002575156149298">"వీడియో కెమెరా"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"ఈ ప్రొఫైల్ నుండి కాల్ చేయడం సాధ్యపడలేదు"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"మీ వర్క్ పాలసీ, మిమ్మల్ని వర్క్ ప్రొఫైల్ నుండి మాత్రమే ఫోన్ కాల్స్ చేయడానికి అనుమతిస్తుంది"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"వర్క్ ప్రొఫైల్కు మారండి"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"మూసివేయండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e72471b..ff1bf35 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"อัตโนมัติ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ไม่มีเสียงหรือการสั่น"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ไม่มีเสียงหรือการสั่น และปรากฏต่ำลงมาในส่วนการสนทนา"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าโทรศัพท์"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าโทรศัพท์ การสนทนาจาก <xliff:g id="APP_NAME">%1$s</xliff:g> จะแสดงเป็นบับเบิลโดยค่าเริ่มต้น"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าอุปกรณ์"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าอุปกรณ์ การสนทนาจาก <xliff:g id="APP_NAME">%1$s</xliff:g> จะแสดงเป็นบับเบิลโดยค่าเริ่มต้น"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ให้ระบบพิจารณาว่าจะให้การแจ้งเตือนนี้ส่งเสียงหรือสั่นหรือไม่"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>สถานะ:</b> เลื่อนระดับเป็นค่าเริ่มต้น"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>สถานะ:</b> ลดระดับเป็นปิดเสียง"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"ปานกลาง"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"เล็ก"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"ใหญ่"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"เสร็จสิ้น"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"แก้ไข"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"การตั้งค่าหน้าต่างแว่นขยาย"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"แตะเพื่อเปิดฟีเจอร์การช่วยเหลือพิเศษ ปรับแต่งหรือแทนที่ปุ่มนี้ในการตั้งค่า\n\n"<annotation id="link">"ดูการตั้งค่า"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"เปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> จาก <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"เลิกทำ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ขยับไปใกล้มากขึ้นเพื่อเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ขยับไปใกล้ <xliff:g id="DEVICENAME">%1$s</xliff:g> มากขึ้นเพื่อเล่นที่นี่"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"กำลังเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"เกิดข้อผิดพลาด โปรดลองอีกครั้ง"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"กำลังโหลด"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ลำโพงและจอแสดงผล"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"อุปกรณ์ที่แนะนำ"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"ต้องใช้บัญชี Premium"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"วิธีการทำงานของการออกอากาศ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ประกาศ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ผู้ที่อยู่ใกล้คุณและมีอุปกรณ์บลูทูธที่รองรับสามารถรับฟังสื่อที่คุณกำลังออกอากาศได้"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ หน้าจอนี้จะปิดไป"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"อุปกรณ์ที่พับได้กำลังกางออก"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"อุปกรณ์ที่พับได้กำลังพลิกไปมา"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"เหลือแบตเตอรี่ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"เชื่อมต่อสไตลัสกับที่ชาร์จ"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"แบตเตอรี่สไตลัสเหลือน้อย"</string>
+ <string name="video_camera" msgid="7654002575156149298">"กล้องวิดีโอ"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"โทรจากโปรไฟล์นี้ไม่ได้"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"นโยบายการทำงานอนุญาตให้คุณโทรออกได้จากโปรไฟล์งานเท่านั้น"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"สลับไปใช้โปรไฟล์งาน"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"ปิด"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index d8f5bad..a287d20 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Awtomatiko"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Walang tunog o pag-vibrate"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Walang tunog o pag-vibrate at lumalabas nang mas mababa sa seksyon ng pag-uusap"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng telepono"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng telepono. Mga pag-uusap mula sa <xliff:g id="APP_NAME">%1$s</xliff:g> bubble bilang default."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ipatukoy sa system kung dapat gumawa ng tunog o pag-vibrate ang notification na ito"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> Na-promote sa Default"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Status:</b> Na-demote sa Naka-silent"</string>
@@ -812,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Katamtaman"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Maliit"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Malaki"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Tapos na"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"I-edit"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Mga setting ng window ng magnifier"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"I-tap, buksan mga feature ng accessibility. I-customize o palitan button sa Mga Setting.\n\n"<annotation id="link">"Tingnan ang mga setting"</annotation></string>
@@ -885,8 +886,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"I-undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Lumapit pa para mag-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para mag-play dito, lumapit sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Nagpe-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Nagkaproblema. Subukan ulit."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Naglo-load"</string>
@@ -915,6 +915,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Mga Speaker at Display"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Mga Iminumungkahing Device"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Paano gumagana ang pag-broadcast"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Makakapakinig ang mga taong malapit sa iyo na may mga compatible na Bluetooth device sa media na bino-broadcast mo"</string>
@@ -1056,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Mag-o-off ang screen na ito"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ina-unfold na foldable na device"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Fini-flip na foldable na device"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterya na lang ang natitira"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ikonekta sa charger ang iyong stylus"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Paubos na ang baterya ng stylus"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 52088dbb..de21f2c 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alt sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sol sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sağ sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"İş profilindeki ekran görüntüleri <xliff:g id="APP">%1$s</xliff:g> uygulamasına kaydedilir"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Dosyalar"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran kaydı işleniyor"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Otomatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sessiz veya titreşim yok"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ses veya titreşim yok, görüşme bölümünün altında görünür"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Telefon ayarlarına bağlı olarak zili çalabilir veya titreyebilir"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Telefon ayarlarına bağlı olarak zili çalabilir veya titreyebilir <xliff:g id="APP_NAME">%1$s</xliff:g> adlı uygulamadan görüşmeler varsayılan olarak baloncukla gösterilir."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirimin ses çıkarması veya titreşmesi gerekip gerekmediğine sistem karar versin"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Durum:</b> Varsayılana yükseltildi"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Durum:</b> Sessize Düşürüldü"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> uygulamasından <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısını çal"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Geri al"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oynatmak için yaklaşın"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Burada oynatmak için <xliff:g id="DEVICENAME">%1$s</xliff:g> cihazına yaklaşın"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oynatılıyor"</string>
- <string name="media_transfer_failed" msgid="7955354964610603723">"Bir sorun oldu. Tekrar deneyin."</string>
+ <string name="media_transfer_failed" msgid="7955354964610603723">"Bir hata oluştu. Tekrar deneyin."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Yükleme"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Devre dışı, uygulamaya bakın"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Bulunamadı"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrol kullanılamıyor"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ses düzeyi"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hoparlörler ve Ekranlar"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Önerilen Cihazlar"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayınlamanın işleyiş şekli"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Anons"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ * Bu ekran kapatılacak"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Katlanabilir cihaz açılıyor"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Katlanabilir cihaz döndürülüyor"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> pil kaldı"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ekran kaleminizi bir şarj cihazına bağlayın"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düşük"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 9638c65..61fc89c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Знизу на <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Зліва на <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Справа на <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Робочі знімки екрана зберігаються в додатку <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Файли"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запис відео з екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обробка записування екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звуку чи вібрації"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звуку чи вібрації, з\'являється нижче в розділі розмов"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Дзвінок або вібрація залежно від налаштувань телефона"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Дзвінок або вібрація залежно від налаштувань телефона. Розмови з додатка <xliff:g id="APP_NAME">%1$s</xliff:g> за умовчанням з\'являються як спливаючий чат."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволити системі визначати, чи має сповіщення супроводжуватися звуком або вібрацією"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус</b>: підвищено до \"За умовчанням\""</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус</b>: знижено до \"Без звуку\""</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Увімкнути пісню \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" у додатку <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Відмінити"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Щоб відтворити контент на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>, наблизьтеся до нього"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Щоб відтворити на цьому пристрої, перемістіть його ближче до пристрою \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Відтворюється на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Сталася помилка. Повторіть спробу."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Завантаження"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"планшет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, перейдіть у додаток"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знайдено"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Елемент керування недоступний"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Гучність"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки й екрани"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Пропоновані пристрої"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як працює трансляція"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Трансляція"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Цей екран вимкнеться"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Розкладний пристрій у розкладеному стані"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Розкладний пристрій обертається"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Заряд акумулятора: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Підключіть стилус до зарядного пристрою"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Низький заряд акумулятора стилуса"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 46acc98..bec50f1 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"نیچے کا احاطہ <xliff:g id="PERCENT">%1$d</xliff:g> فیصد"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"بایاں احاطہ <xliff:g id="PERCENT">%1$d</xliff:g> فیصد"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"دایاں احاطہ <xliff:g id="PERCENT">%1$d</xliff:g> فیصد"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"دفتری اسکرین شاٹس کو <xliff:g id="APP">%1$s</xliff:g> ایپ میں محفوظ کیا جاتا ہے"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"فائلز"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"اسکرین ریکارڈر"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"سکرین ریکارڈنگ پروسیس ہورہی ہے"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اسکرین ریکارڈ سیشن کیلئے جاری اطلاع"</string>
@@ -545,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"کوئی آواز یا وائبریشن نہیں"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"کوئی آواز یا وائبریشن نہیں اور گفتگو کے سیکشن میں نیچے ظاہر ہوتا ہے"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"آپ کے آلہ کی ترتیبات کے مطابق وائبریٹ یا گھنٹی بج سکتی ہے"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"فون کی ترتیبات کے مطابق وائبریٹ یا گھنٹی بج سکتی ہے۔ بذریعہ ڈیفالٹ <xliff:g id="APP_NAME">%1$s</xliff:g> بلبلہ سے گفتگوئیں۔"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"آلے کی ترتیبات کی بنیاد پر وائبریٹ یا گھنٹی بج سکتی ہے"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"آلے کی ترتیبات بنیاد پر وائبریٹ یا گھنٹی بج سکتی ہے۔ بذریعہ ڈیفالٹ <xliff:g id="APP_NAME">%1$s</xliff:g> بلبلہ سے گفتگوئیں۔"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سسٹم کو اس بات کا تعین کرنے دیں کہ آیا اس اطلاع کی آواز ہو یا وائبریٹ ہونا چاہیے"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> ڈیفالٹ پر درجہ بند کیا گیا"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>اسٹیٹس:</b> کو خاموش پر درجہ بند کیا گیا"</string>
@@ -814,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"متوسط"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"چھوٹا"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"بڑا"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"ہو گیا"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"ترمیم کریں"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"میگنیفائر ونڈو کی ترتیبات"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ایکسیسبیلٹی خصوصیات کھولنے کے لیے تھپتھپائیں۔ ترتیبات میں اس بٹن کو حسب ضرورت بنائیں یا تبدیل کریں۔\n\n"<annotation id="link">"ترتیبات ملاحظہ کریں"</annotation></string>
@@ -887,13 +884,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> سے <xliff:g id="SONG_NAME">%1$s</xliff:g> چلائیں"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"کالعدم کریں"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چلانے کے لیے قریب کریں"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"یہاں چلانے کے لیے، <xliff:g id="DEVICENAME">%1$s</xliff:g> کے زیادہ جائیں"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چل رہا ہے"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"کچھ غلط ہوگیا۔ پھر کوشش کریں۔"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"لوڈ ہو رہا ہے"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ٹیبلیٹ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غیر فعال، ایپ چیک کریں"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"نہیں ملا"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"کنٹرول دستیاب نہیں ہے"</string>
@@ -917,8 +912,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"والیوم"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"اسپیکرز اور ڈسپلیز"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
- <skip />
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"تجویز کردہ آلات"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"پریمئیم اکاؤنٹ درکار ہے"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"براڈکاسٹنگ کیسے کام کرتا ہے"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"براڈکاسٹ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"موافق بلوٹوتھ آلات کے ساتھ آپ کے قریبی لوگ آپ کے نشر کردہ میڈیا کو سن سکتے ہیں"</string>
@@ -1062,4 +1057,10 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"فولڈ ہونے والے آلے کو گھمایا جا رہا ہے"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> بیٹری باقی ہے"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"اپنے اسٹائلس کو چارجر منسلک کریں"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"اسٹائلس بیٹری کم ہے"</string>
+ <string name="video_camera" msgid="7654002575156149298">"ویڈیو کیمرا"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"اس پروفائل سے کال نہیں کر سکتے"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"آپ کے کام سے متعلق پالیسی آپ کو صرف دفتری پروفائل سے فون کالز کرنے کی اجازت دیتی ہے"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"دفتری پروفائل پر سوئچ کریں"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"بند کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index ef7dbad..939cb5c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -543,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Tovush yoki tebranishsiz"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tovush yoki tebranishsiz hamda suhbatlar ruknining pastida chiqadi"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Telefon sozlamalari asosida jiringlashi yoki tebranishi mumkin"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Telefon sozlamalari asosida jiringlashi yoki tebranishi mumkin. <xliff:g id="APP_NAME">%1$s</xliff:g> suhbatlari standart holatda bulutcha shaklida chiqadi."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirishnoma jiringlashi yoki tebranishini hal qilsin"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Holati:</b> Birlamchi darajaga chiqarildi"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Holati:</b> Sokin darajaga tushirildi"</string>
@@ -812,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Oʻrtacha"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Kichik"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Yirik"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Tayyor"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Tahrirlash"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Lupa oynasi sozlamalari"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Maxsus imkoniyatlarni ochish uchun bosing Sozlamalardan moslay yoki almashtira olasiz.\n\n"<annotation id="link">"Sozlamalar"</annotation></string>
@@ -914,6 +915,8 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Karnaylar va displeylar"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Taklif qilingan qurilmalar"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Translatsiya qanday ishlaydi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Translatsiya"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Atrofingizdagi mos Bluetooth qurilmasiga ega foydalanuvchilar siz translatsiya qilayotgan mediani tinglay olishadi"</string>
@@ -1057,4 +1060,15 @@
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Buklanadigan qurilma aylantirilmoqda"</string>
<string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batareya quvvati: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Stilusni quvvat manbaiga ulang"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus batareyasi kam"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 4ba82a1..2326813 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Cạnh dưới cùng <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Cạnh trái <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Cạnh phải <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Ảnh chụp màn hình công việc được lưu trong ứng dụng <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đang xử lý video ghi màn hình"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Thông báo đang diễn ra về phiên ghi màn hình"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Tự động"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Không phát âm thanh hoặc rung"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Không phát âm thanh hoặc rung và xuất hiện phía dưới trong phần cuộc trò chuyện"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Có thể đổ chuông hoặc rung tùy theo chế độ cài đặt trên điện thoại"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Có thể đổ chuông hoặc rung tùy theo chế độ cài đặt trên điện thoại. Các cuộc trò chuyện từ <xliff:g id="APP_NAME">%1$s</xliff:g> sẽ hiện ở dạng bong bóng theo mặc định."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Cho phép hệ thống quyết định xem thông báo này phát âm thanh hay rung"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Trạng thái:</b> Đã thay đổi thành Mặc định"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Trạng thái:</b> Đã thay đổi thành Im lặng"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Vừa"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Nhỏ"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Lớn"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Xong"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Chỉnh sửa"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Chế độ cài đặt cửa sổ phóng to"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Nhấn để mở bộ tính năng hỗ trợ tiếp cận. Tuỳ chỉnh/thay thế nút này trong phần Cài đặt.\n\n"<annotation id="link">"Xem chế độ cài đặt"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> trên <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Hủy"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Đưa thiết bị đến gần hơn để phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Để phát ở đây, vui lòng lại gần <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Đang phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Đã xảy ra lỗi. Hãy thử lại."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Đang tải"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"máy tính bảng"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Không hoạt động, hãy kiểm tra ứng dụng"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Không tìm thấy"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Không có chức năng điều khiển"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Âm lượng"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Loa và màn hình"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Thiết bị được đề xuất"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cách tính năng truyền hoạt động"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Truyền"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Màn hình này sẽ tắt"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Thiết bị có thể gập lại đang được mở ra"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Thiết bị có thể gập lại đang được lật ngược"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Còn <xliff:g id="PERCENTAGE">%s</xliff:g> pin"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hãy kết nối bút cảm ứng với bộ sạc"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Bút cảm ứng bị yếu pin"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 4426839..bfc6cc5 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"底部边界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"左侧边界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"右侧边界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"工作屏幕截图保存在“<xliff:g id="APP">%1$s</xliff:g>”应用中"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"文件"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在处理屏幕录制视频"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"自动"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"不发出提示音,也不振动"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"不发出提示音,也不振动;显示在对话部分的靠下位置"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"可能会响铃或振动(取决于手机设置)"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能会响铃或振动(取决于手机设置)。默认情况下,来自<xliff:g id="APP_NAME">%1$s</xliff:g>的对话会以对话泡的形式显示。"</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"让系统决定是否应让设备在收到此通知时发出提示音或振动"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>状态</b>:已提升为“默认”"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>状态</b>:已降低为“静音”"</string>
@@ -887,13 +887,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"通过<xliff:g id="APP_LABEL">%2$s</xliff:g>播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"撤消"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"若要在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放,请靠近这台设备"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"若要在此设备上播放,请再靠近<xliff:g id="DEVICENAME">%1$s</xliff:g>一点"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"正在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"出了点问题,请重试。"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"正在加载"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"平板电脑"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"无效,请检查应用"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"未找到"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"控件不可用"</string>
@@ -917,7 +915,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"音箱和显示屏"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建议的设备"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"广播的运作方式"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"广播"</string>
@@ -1060,8 +1059,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 此屏幕将会关闭"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展开可折叠设备"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻转可折叠设备"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"电池还剩 <xliff:g id="PERCENTAGE">%s</xliff:g> 的电量"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"请将触控笔连接充电器"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"触控笔电池电量低"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 04a06bc..3f14f5e 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"無音效或震動"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"無音效或震動,並在對話部分的較低位置顯示"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"根據手機設定發出鈴聲或震動"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機設定發出鈴聲或震動。「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會預設以對話氣泡顯示。"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"根據裝置的設定響鈴或震動"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"根據裝置的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"由系統判斷是否要讓此通知發出音效或震動"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>狀態:</b>已提升為預設"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>狀態:</b>已降低為靜音"</string>
@@ -812,15 +812,14 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"中"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"小"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"大"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"完成"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"編輯"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大鏡視窗設定"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"㩒一下就可以開無障礙功能。喺「設定」度自訂或者取代呢個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣即可暫時隱藏"</string>
<string name="accessibility_floating_button_undo" msgid="511112888715708241">"復原"</string>
- <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"已移除「<xliff:g id="FEATURE_NAME">%s</xliff:g>」捷徑"</string>
- <string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{已移除 # 個捷徑}other{已移除 # 個捷徑}}"</string>
+ <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"已移除「<xliff:g id="FEATURE_NAME">%s</xliff:g>」快速鍵"</string>
+ <string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{已移除 # 個快速鍵}other{已移除 # 個快速鍵}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移去左上方"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移去右上方"</string>
<string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"移到左下方"</string>
@@ -885,11 +884,10 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"在 <xliff:g id="APP_LABEL">%2$s</xliff:g> 播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"如要在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放,請靠近一點"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"如要在這部裝置播放,請靠近「<xliff:g id="DEVICENAME">%1$s</xliff:g>」一點"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"正在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"發生錯誤,請再試一次。"</string>
- <string name="media_transfer_loading" msgid="5544017127027152422">"載入中"</string>
+ <string name="media_transfer_loading" msgid="5544017127027152422">"正在載入"</string>
<string name="media_ttt_default_device_type" msgid="4457646436153370169">"平板電腦"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"已停用,請檢查應用程式"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"找不到"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"必須有付費帳戶"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播運作方式"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近有兼容藍牙裝置的人可收聽您正在廣播的媒體內容"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 此螢幕將關閉"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展開折疊式裝置"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻轉折疊式裝置"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"剩餘電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆連接充電器"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電量不足"</string>
+ <string name="video_camera" msgid="7654002575156149298">"攝影機"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"無法透過這個資料夾撥打電話"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"貴公司政策僅允許透過工作資料夾撥打電話"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"切換至工作資料夾"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"關閉"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 1df1cfb..e02597f 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -543,8 +543,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"不震動或發出聲音"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並顯示在對話區的下方"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"根據手機的設定響鈴或震動"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"根據裝置的設定響鈴或震動"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"根據裝置的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"由系統判斷要讓裝置在收到這則通知時震動還是發出音效"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>狀態:</b>已提升為預設"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>狀態:</b>已降低為靜音"</string>
@@ -812,8 +812,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"中"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"小"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"大"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"完成"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"編輯"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大鏡視窗設定"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"輕觸即可開啟無障礙功能。你可以前往「設定」自訂或更換這個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
@@ -885,8 +884,7 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"透過「<xliff:g id="APP_LABEL">%2$s</xliff:g>」播放〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"如要在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放,請靠近一點"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"如要在這部裝置播放,請移到更靠近「<xliff:g id="DEVICENAME">%1$s</xliff:g>」的位置"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"正在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"發生錯誤,請再試一次。"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"載入中"</string>
@@ -915,6 +913,7 @@
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
+ <string name="media_output_status_require_premium" msgid="5691200962588753380">"必須有付費帳戶"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播功能的運作方式"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"如果附近的人有相容的藍牙裝置,就可以聽到你正在廣播的媒體內容"</string>
@@ -1056,8 +1055,12 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 這麼做會關閉這個螢幕"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展開的折疊式裝置"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻轉折疊式裝置"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
- <skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"剩餘電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆接上充電器"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電力不足"</string>
+ <string name="video_camera" msgid="7654002575156149298">"攝影機"</string>
+ <string name="call_from_work_profile_title" msgid="6991157106804289643">"無法透過這個資料夾撥打電話"</string>
+ <string name="call_from_work_profile_text" msgid="3458704745640229638">"貴公司政策僅允許透過工作資料夾撥打電話"</string>
+ <string name="call_from_work_profile_action" msgid="2937701298133010724">"切換至工作資料夾"</string>
+ <string name="call_from_work_profile_close" msgid="7927067108901068098">"關閉"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a0153b3..a68b214 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -91,10 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ophansi"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ongakwesobunxele"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ongakwesokudla"</string>
- <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
- <skip />
- <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
- <skip />
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Izithombe-skrini zomsebenzi zigcinwa ku-app ye-<xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Amafayela"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Icubungula okokuqopha iskrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</string>
@@ -545,8 +543,10 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Okuzenzekelayo"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Awukho umsindo noma ukudlidliza"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Awukho umsindo noma ukudlidliza futhi ivela ngezansi esigabeni sengxoxo"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Ingase ikhale noma idlidlize kuya ngamasethingi wefoni yakho"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Ingase ikhale noma idlidlize kuya ngamasethingi wefoni yakho. Izingxoxo ezivela ku-<xliff:g id="APP_NAME">%1$s</xliff:g> ziba yibhamuza ngokuzenzakalela."</string>
+ <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
+ <skip />
+ <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
+ <skip />
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Vumela isistimu inqume uma lesi saziso kufanele senze umsindo noma sidlidlize"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Isimo:</b> Siphromothelwe Kokuzenzakalelayo"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Isimo:</b> Sehliselwe Kokuthulile"</string>
@@ -814,8 +814,7 @@
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Kumaphakathi"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Esincane"</string>
<string name="accessibility_magnification_large" msgid="6602944330021308774">"Obukhulu"</string>
- <!-- no translation found for accessibility_magnification_done (263349129937348512) -->
- <skip />
+ <string name="accessibility_magnification_done" msgid="263349129937348512">"Kwenziwe"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Hlela"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Amasethingi ewindi lesikhulisi"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Thepha ukuze uvule izakhi zokufinyelela. Enza ngendlela oyifisayo noma shintsha le nkinobho Kumasethingi.\n\n"<annotation id="link">"Buka amasethingi"</annotation></string>
@@ -887,13 +886,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Hlehlisa"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Sondeza eduze ukudlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
- <skip />
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ukuze udlale lapha, sondela ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Idlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Kukhona okungahambanga kahle. Zama futhi."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Iyalayisha"</string>
- <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
- <skip />
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ithebulethi"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ayitholakali"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Ukulawula akutholakali"</string>
@@ -917,7 +914,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ivolumu"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Izipikha Neziboniso"</string>
- <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Amadivayisi Aphakanyisiwe"</string>
+ <!-- no translation found for media_output_status_require_premium (5691200962588753380) -->
<skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Indlela ukusakaza okusebenza ngayo"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Sakaza"</string>
@@ -1060,8 +1058,17 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Lesi sikrini sizovala"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Idivayisi egoqekayo iyembulwa"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Idivayisi egoqekayo iphendulwa nxazonke"</string>
- <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ibhethri elisele"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Xhuma i-stylus yakho kushaja"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string>
+ <!-- no translation found for video_camera (7654002575156149298) -->
<skip />
- <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
+ <skip />
+ <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
<skip />
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 3c2453e..d01bc4a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -827,4 +827,8 @@
<item>bottom_end:wallet</item>
</string-array>
+ <!-- Package name for the app that implements the wallpaper picker. -->
+ <string name="config_wallpaperPickerPackage" translatable="false">
+ com.android.wallpaper
+ </string>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f122805..ade75cb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1678,4 +1678,10 @@
<dimen name="rear_display_animation_height">200dp</dimen>
<dimen name="rear_display_title_top_padding">24dp</dimen>
<dimen name="rear_display_title_bottom_padding">16dp</dimen>
+
+ <!--
+ Vertical distance between the pointer and the popup menu that shows up on the lock screen when
+ it is long-pressed.
+ -->
+ <dimen name="keyguard_long_press_settings_popup_vertical_offset">96dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index c5ffc94..6354752 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -33,8 +33,6 @@
<!-- Whether to show chipbar UI whenever the device is unlocked by ActiveUnlock. -->
<bool name="flag_active_unlock_chipbar">true</bool>
- <bool name="flag_smartspace">false</bool>
-
<!-- Whether the user switcher chip shows in the status bar. When true, the multi user
avatar will no longer show on the lockscreen -->
<bool name="flag_user_switcher_chip">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3d3bb6e..d14207a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2342,6 +2342,14 @@
<!-- Removed control in management screen [CHAR LIMIT=20] -->
<string name="controls_removed">Removed</string>
+ <!-- Title for the dialog presented to the user to authorize this app to display a Device
+ controls panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=30] -->
+ <string name="controls_panel_authorization_title">Add <xliff:g id="appName" example="My app">%s</xliff:g>?</string>
+
+ <!-- Shows in a dialog presented to the user to authorize this app to display a Device controls
+ panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=NONE] -->
+ <string name="controls_panel_authorization">When you add <xliff:g id="appName" example="My app">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here.</string>
+
<!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] -->
<string name="accessibility_control_favorite">Favorited</string>
<!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] -->
@@ -2491,6 +2499,8 @@
<string name="controls_menu_add">Add controls</string>
<!-- Controls menu, edit [CHAR_LIMIT=30] -->
<string name="controls_menu_edit">Edit controls</string>
+ <!-- Controls menu, add another app [CHAR LIMIT=30] -->
+ <string name="controls_menu_add_another_app">Add app</string>
<!-- Title for the media output dialog with media related devices [CHAR LIMIT=50] -->
<string name="media_output_dialog_add_output">Add outputs</string>
@@ -2919,4 +2929,13 @@
<!-- Label for the close button on switch to work profile dialog. Switch to work profile dialog guide users to make call from work
profile dialer app as it's not possible to make call from current profile due to an admin policy.[CHAR LIMIT=60] -->
<string name="call_from_work_profile_close">Close</string>
+
+ <!--
+ Label for a menu item in a menu that is shown when the user wishes to configure the lock screen.
+ Clicking on this menu item takes the user to a screen where they can modify the settings of the
+ lock screen.
+
+ [CHAR LIMIT=32]
+ -->
+ <string name="lock_screen_settings">Lock screen settings</string>
</resources>
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index d97031f..52a98984 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -41,9 +41,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/privacy_container"
app:layout_constraintBottom_toBottomOf="@id/carrier_group"
- app:layout_constraintEnd_toStartOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
<Transform
android:scaleX="2.57"
@@ -62,18 +59,18 @@
/>
</Constraint>
+ <!-- LargeScreenShadeHeaderController helps with managing clock width to layout this view -->
<Constraint
android:id="@+id/carrier_group">
<Layout
- app:layout_constraintWidth_min="48dp"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintWidth_min="48dp"
+ app:layout_constraintWidth_default="wrap"
+ app:layout_constraintStart_toStartOf="@id/clock"
app:layout_constraintTop_toBottomOf="@id/privacy_container"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="1"
app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
<PropertySet
android:alpha="1"
diff --git a/packages/SystemUI/scripts/token_alignment/.eslintrc.json b/packages/SystemUI/scripts/token_alignment/.eslintrc.json
new file mode 100644
index 0000000..69dc00e
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/.eslintrc.json
@@ -0,0 +1,31 @@
+{
+ "env": {
+ "es2021": true,
+ "node": true
+ },
+ "parserOptions": {
+ "ecmaVersion": "latest",
+ "sourceType": "module"
+ },
+ "plugins": ["prettier", "@typescript-eslint", "eslint-plugin-simple-import-sort", "import"],
+ "extends": ["prettier", "eslint:recommended", "plugin:@typescript-eslint/recommended"],
+ "rules": {
+ "prettier/prettier": ["error"],
+ "no-unused-vars": "off",
+ "@typescript-eslint/no-unused-vars": [
+ "warn",
+ {
+ "argsIgnorePattern": "^_",
+ "varsIgnorePattern": "^_",
+ "caughtErrorsIgnorePattern": "^_"
+ }
+ ],
+ "no-multiple-empty-lines": ["error", { "max": 2 }],
+ "no-multi-spaces": "error",
+ "simple-import-sort/imports": "error",
+ "simple-import-sort/exports": "error",
+ "import/first": "error",
+ "import/newline-after-import": "error",
+ "import/no-duplicates": "error"
+ }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/.gitignore b/packages/SystemUI/scripts/token_alignment/.gitignore
new file mode 100644
index 0000000..96ce14f
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/.gitignore
@@ -0,0 +1,2 @@
+vscode
+node_modules
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/.prettierrc b/packages/SystemUI/scripts/token_alignment/.prettierrc
new file mode 100644
index 0000000..20f02f9
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/.prettierrc
@@ -0,0 +1,9 @@
+{
+ "tabWidth": 4,
+ "printWidth": 100,
+ "semi": true,
+ "singleQuote": true,
+ "bracketSameLine": true,
+ "bracketSpacing": true,
+ "arrowParens": "always"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/DOMFuncs.ts b/packages/SystemUI/scripts/token_alignment/helpers/DOMFuncs.ts
new file mode 100644
index 0000000..80e075c
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/DOMFuncs.ts
@@ -0,0 +1,297 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+type IElementComment =
+ | { commentNode: undefined; textContent: undefined; hidden: undefined }
+ | { commentNode: Node; textContent: string; hidden: boolean };
+
+interface ITag {
+ attrs?: Record<string, string | number>;
+ tagName: string;
+}
+
+export interface INewTag extends ITag {
+ content?: string | number;
+ comment?: string;
+}
+
+export type IUpdateTag = Partial<Omit<INewTag, 'tagName'>>;
+
+export default class DOM {
+ static addEntry(containerElement: Element, tagOptions: INewTag) {
+ const doc = containerElement.ownerDocument;
+ const exists = this.alreadyHasEntry(containerElement, tagOptions);
+
+ if (exists) {
+ console.log('Ignored adding entry already available: ', exists.outerHTML);
+ return;
+ }
+
+ let insertPoint: Node | null = containerElement.lastElementChild; //.childNodes[containerElement.childNodes.length - 1];
+
+ if (!insertPoint) {
+ console.log('Ignored adding entry in empity parent: ', containerElement.outerHTML);
+ return;
+ }
+
+ const { attrs, comment, content, tagName } = tagOptions;
+
+ if (comment) {
+ const commentNode = doc.createComment(comment);
+ this.insertAfterIdented(commentNode, insertPoint);
+ insertPoint = commentNode;
+ }
+
+ const newEl = doc.createElement(tagName);
+ if (content) newEl.innerHTML = content.toString();
+ if (attrs)
+ Object.entries(attrs).forEach(([attr, value]) =>
+ newEl.setAttribute(attr, value.toString())
+ );
+ this.insertAfterIdented(newEl, insertPoint);
+
+ return true;
+ }
+
+ static insertBeforeIndented(newNode: Node, referenceNode: Node) {
+ const paddingNode = referenceNode.previousSibling;
+ const ownerDoc = referenceNode.ownerDocument;
+ const containerNode = referenceNode.parentNode;
+
+ if (!paddingNode || !ownerDoc || !containerNode) return;
+
+ const currentPadding = paddingNode.textContent || '';
+ const textNode = referenceNode.ownerDocument.createTextNode(currentPadding);
+
+ containerNode.insertBefore(newNode, referenceNode);
+ containerNode.insertBefore(textNode, newNode);
+ }
+
+ static insertAfterIdented(newNode: Node, referenceNode: Node) {
+ const paddingNode = referenceNode.previousSibling;
+ const ownerDoc = referenceNode.ownerDocument;
+ const containerNode = referenceNode.parentNode;
+
+ if (!paddingNode || !ownerDoc || !containerNode) return;
+
+ const currentPadding = paddingNode.textContent || '';
+ const textNode = ownerDoc.createTextNode(currentPadding);
+
+ containerNode.insertBefore(newNode, referenceNode.nextSibling);
+ containerNode.insertBefore(textNode, newNode);
+ }
+
+ static getElementComment(el: Element): IElementComment {
+ const commentNode = el.previousSibling?.previousSibling;
+
+ const out = { commentNode: undefined, textContent: undefined, hidden: undefined };
+
+ if (!commentNode) return out;
+
+ const textContent = commentNode.textContent || '';
+ const hidden = textContent.substring(textContent.length - 6) == '@hide ';
+
+ if (!(commentNode && commentNode.nodeName == '#comment')) return out;
+
+ return { commentNode, textContent, hidden: hidden };
+ }
+
+ static duplicateEntryWithChange(
+ templateElement: Element,
+ options: Omit<IUpdateTag, 'content'>
+ ) {
+ const exists = this.futureEntryAlreadyExist(templateElement, options);
+ if (exists) {
+ console.log('Ignored duplicating entry already available: ', exists.outerHTML);
+ return;
+ }
+
+ const { commentNode } = this.getElementComment(templateElement);
+ let insertPoint: Node = templateElement;
+
+ if (commentNode) {
+ const newComment = commentNode.cloneNode();
+ this.insertAfterIdented(newComment, insertPoint);
+ insertPoint = newComment;
+ }
+
+ const newEl = templateElement.cloneNode(true) as Element;
+ this.insertAfterIdented(newEl, insertPoint);
+
+ this.updateElement(newEl, options);
+ return true;
+ }
+
+ static replaceStringInAttributeValueOnQueried(
+ root: Element,
+ query: string,
+ attrArray: string[],
+ replaceMap: Map<string, string>
+ ): boolean {
+ let updated = false;
+ const queried = [...Array.from(root.querySelectorAll(query)), root];
+
+ queried.forEach((el) => {
+ attrArray.forEach((attr) => {
+ if (el.hasAttribute(attr)) {
+ const currentAttrValue = el.getAttribute(attr);
+
+ if (!currentAttrValue) return;
+
+ [...replaceMap.entries()].some(([oldStr, newStr]) => {
+ if (
+ currentAttrValue.length >= oldStr.length &&
+ currentAttrValue.indexOf(oldStr) ==
+ currentAttrValue.length - oldStr.length
+ ) {
+ el.setAttribute(attr, currentAttrValue.replace(oldStr, newStr));
+ updated = true;
+ return true;
+ }
+ return false;
+ });
+ }
+ });
+ });
+
+ return updated;
+ }
+
+ static updateElement(el: Element, updateOptions: IUpdateTag) {
+ const exists = this.futureEntryAlreadyExist(el, updateOptions);
+ if (exists) {
+ console.log('Ignored updating entry already available: ', exists.outerHTML);
+ return;
+ }
+
+ const { comment, attrs, content } = updateOptions;
+
+ if (comment) {
+ const { commentNode } = this.getElementComment(el);
+ if (commentNode) {
+ commentNode.textContent = comment;
+ }
+ }
+
+ if (attrs) {
+ for (const attr in attrs) {
+ const value = attrs[attr];
+
+ if (value != undefined) {
+ el.setAttribute(attr, `${value}`);
+ } else {
+ el.removeAttribute(attr);
+ }
+ }
+ }
+
+ if (content != undefined) {
+ el.innerHTML = `${content}`;
+ }
+
+ return true;
+ }
+
+ static elementToOptions(el: Element): ITag {
+ return {
+ attrs: this.getAllElementAttributes(el),
+ tagName: el.tagName,
+ };
+ }
+
+ static getAllElementAttributes(el: Element): Record<string, string> {
+ return el
+ .getAttributeNames()
+ .reduce(
+ (acc, attr) => ({ ...acc, [attr]: el.getAttribute(attr) || '' }),
+ {} as Record<string, string>
+ );
+ }
+
+ static futureEntryAlreadyExist(el: Element, updateOptions: IUpdateTag) {
+ const currentElOptions = this.elementToOptions(el);
+
+ if (!el.parentElement) {
+ console.log('Checked el has no parent');
+ process.exit();
+ }
+
+ return this.alreadyHasEntry(el.parentElement, {
+ ...currentElOptions,
+ ...updateOptions,
+ attrs: { ...currentElOptions.attrs, ...updateOptions.attrs },
+ });
+ }
+
+ static alreadyHasEntry(
+ containerElement: Element,
+ { attrs, tagName }: Pick<INewTag, 'attrs' | 'tagName'>
+ ) {
+ const qAttrs = attrs
+ ? Object.entries(attrs)
+ .map(([a, v]) => `[${a}="${v}"]`)
+ .join('')
+ : '';
+
+ return containerElement.querySelector(tagName + qAttrs);
+ }
+
+ static replaceContentTextOnQueried(
+ root: Element,
+ query: string,
+ replacePairs: Array<[string, string]>
+ ) {
+ let updated = false;
+ let queried = Array.from(root.querySelectorAll(query));
+
+ if (queried.length == 0) queried = [...Array.from(root.querySelectorAll(query)), root];
+
+ queried.forEach((el) => {
+ replacePairs.forEach(([oldStr, newStr]) => {
+ if (el.innerHTML == oldStr) {
+ el.innerHTML = newStr;
+ updated = true;
+ }
+ });
+ });
+
+ return updated;
+ }
+
+ static XMLDocToString(doc: XMLDocument) {
+ let str = '';
+
+ doc.childNodes.forEach((node) => {
+ switch (node.nodeType) {
+ case 8: // comment
+ str += `<!--${node.nodeValue}-->\n`;
+ break;
+
+ case 3: // text
+ str += node.textContent;
+ break;
+
+ case 1: // element
+ str += (node as Element).outerHTML;
+ break;
+
+ default:
+ console.log('Unhandled node type: ' + node.nodeType);
+ break;
+ }
+ });
+
+ return str;
+ }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/FileIO.ts b/packages/SystemUI/scripts/token_alignment/helpers/FileIO.ts
new file mode 100644
index 0000000..359e3ab
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/FileIO.ts
@@ -0,0 +1,112 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.import { exec } from 'child_process';
+
+import { exec } from 'child_process';
+import { parse } from 'csv-parse';
+import { promises as fs } from 'fs';
+import jsdom from 'jsdom';
+
+const DOMParser = new jsdom.JSDOM('').window.DOMParser as typeof window.DOMParser;
+
+type TFileList = string[];
+
+export type TCSVRecord = Array<string | boolean | number>;
+
+class _FileIO {
+ public parser = new DOMParser();
+ public saved: string[] = [];
+
+ public loadXML = async (path: string): Promise<XMLDocument> => {
+ try {
+ const src = await this.loadFileAsText(path);
+ return this.parser.parseFromString(src, 'text/xml') as XMLDocument;
+ } catch (error) {
+ console.log(`Failed to parse XML file '${path}'.`, error);
+ process.exit();
+ }
+ };
+
+ public loadFileAsText = async (path: string): Promise<string> => {
+ try {
+ return await fs.readFile(path, { encoding: 'utf8' });
+ } catch (error) {
+ console.log(`Failed to read file '${path}'.`, error);
+ process.exit();
+ }
+ };
+
+ public saveFile = async (data: string, path: string) => {
+ try {
+ await fs.writeFile(path, data, { encoding: 'utf8' });
+ this.saved.push(path);
+ } catch (error) {
+ console.log(error);
+ console.log(`Failed to write file '${path}'.`);
+ process.exit();
+ }
+ };
+
+ public loadFileList = async (path: string): Promise<TFileList> => {
+ const src = await this.loadFileAsText(path);
+
+ try {
+ return JSON.parse(src) as TFileList;
+ } catch (error) {
+ console.log(error);
+ console.log(`Failed to parse JSON file '${path}'.`);
+ process.exit();
+ }
+ };
+
+ public loadCSV = (path: string): Promise<Array<TCSVRecord>> => {
+ return new Promise((resolve, reject) => {
+ this.loadFileAsText(path).then((src) => {
+ parse(
+ src,
+ {
+ delimiter: ' ',
+ },
+ (err, records) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+
+ resolve(records);
+ }
+ );
+ });
+ });
+ };
+
+ formatSaved = () => {
+ const cmd = `idea format ${this.saved.join(' ')}`;
+
+ exec(cmd, (error, out, stderr) => {
+ if (error) {
+ console.log(error.message);
+ return;
+ }
+
+ if (stderr) {
+ console.log(stderr);
+ return;
+ }
+
+ console.log(out);
+ });
+ };
+}
+
+export const FileIO = new _FileIO();
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/migrationList.ts b/packages/SystemUI/scripts/token_alignment/helpers/migrationList.ts
new file mode 100644
index 0000000..8d50644
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/migrationList.ts
@@ -0,0 +1,70 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.import { exec } from 'child_process';
+
+import { FileIO, TCSVRecord } from './FileIO';
+import ProcessArgs from './processArgs';
+
+interface IInputMigItem {
+ migrationToken: string;
+ materialToken: string;
+ newDefaultValue?: string;
+ newComment?: string;
+}
+
+interface IAditionalKeys {
+ step: ('update' | 'duplicate' | 'add' | 'ignore')[];
+ isHidden: boolean;
+ replaceToken: string;
+}
+
+export type IMigItem = Omit<IInputMigItem, 'materialToken' | 'migrationToken'> & IAditionalKeys;
+
+export type IMigrationMap = Map<string, IMigItem>;
+
+function isMigrationRecord(record: TCSVRecord): record is string[] {
+ return !record.some((value) => typeof value != 'string') || record.length != 5;
+}
+
+export const loadMIgrationList = async function (): Promise<IMigrationMap> {
+ const out: IMigrationMap = new Map();
+ const csv = await FileIO.loadCSV('resources/migrationList.csv');
+
+ csv.forEach((record, i) => {
+ if (i == 0) return; // header
+
+ if (typeof record[0] != 'string') return;
+
+ if (!isMigrationRecord(record)) {
+ console.log(`Failed to validade CSV record as string[5].`, record);
+ process.exit();
+ }
+
+ const [originalToken, materialToken, newDefaultValue, newComment, migrationToken] = record;
+
+ if (out.has(originalToken)) {
+ console.log('Duplicated entry on Migration CSV file: ', originalToken);
+ return;
+ }
+
+ out.set(originalToken, {
+ replaceToken: ProcessArgs.isDebug ? migrationToken : materialToken,
+ ...(!!newDefaultValue && { newDefaultValue }),
+ ...(!!newComment && { newComment }),
+ step: [],
+ isHidden: false,
+ });
+ });
+
+ return new Map([...out].sort((a, b) => b[0].length - a[0].length));
+};
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/processArgs.ts b/packages/SystemUI/scripts/token_alignment/helpers/processArgs.ts
new file mode 100644
index 0000000..be0e232
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/processArgs.ts
@@ -0,0 +1,21 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.import { exec } from 'child_process';
+
+const myArgs = process.argv.slice(2);
+
+const ProcessArgs = {
+ isDebug: myArgs.includes('debug'),
+};
+
+export default ProcessArgs;
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/processXML.ts b/packages/SystemUI/scripts/token_alignment/helpers/processXML.ts
new file mode 100644
index 0000000..368d4cb
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/processXML.ts
@@ -0,0 +1,102 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.import { exec } from 'child_process';
+
+import DOM, { INewTag, IUpdateTag } from './DOMFuncs';
+import { FileIO } from './FileIO';
+import { IMigItem, IMigrationMap } from './migrationList';
+
+export type TResultExistingEval = ['update' | 'duplicate', IUpdateTag] | void;
+export type TResultMissingEval = INewTag | void;
+
+interface IProcessXML {
+ attr?: string;
+ containerQuery?: string;
+ evalExistingEntry?: TEvalExistingEntry;
+ evalMissingEntry?: TEvalMissingEntry;
+ hidable?: boolean;
+ path: string;
+ step: number;
+ tagName: string;
+}
+
+export type TEvalExistingEntry = (
+ attrname: string,
+ migItem: IMigItem,
+ qItem: Element
+) => TResultExistingEval;
+
+export type TEvalMissingEntry = (originalToken: string, migItem: IMigItem) => TResultMissingEval;
+
+export async function processQueriedEntries(
+ migrationMap: IMigrationMap,
+ {
+ attr = 'name',
+ containerQuery = '*',
+ evalExistingEntry,
+ path,
+ step,
+ tagName,
+ evalMissingEntry,
+ }: IProcessXML
+) {
+ const doc = await FileIO.loadXML(path);
+
+ const containerElement =
+ (containerQuery && doc.querySelector(containerQuery)) || doc.documentElement;
+
+ migrationMap.forEach((migItem, originalToken) => {
+ migItem.step[step] = 'ignore';
+
+ const queryTiems = containerElement.querySelectorAll(
+ `${tagName}[${attr}="${originalToken}"]`
+ );
+
+ if (evalMissingEntry) {
+ const addinOptions = evalMissingEntry(originalToken, migItem);
+
+ if (queryTiems.length == 0 && containerElement && addinOptions) {
+ DOM.addEntry(containerElement, addinOptions);
+ migItem.step[step] = 'add';
+ return;
+ }
+ }
+
+ if (evalExistingEntry)
+ queryTiems.forEach((qEl) => {
+ const attrName = qEl.getAttribute(attr);
+ const migItem = migrationMap.get(attrName || '');
+
+ if (!attrName || !migItem) return;
+
+ const updateOptions = evalExistingEntry(attrName, migItem, qEl);
+
+ if (!updateOptions) return;
+
+ const [processType, processOptions] = updateOptions;
+
+ switch (processType) {
+ case 'update':
+ if (DOM.updateElement(qEl, processOptions)) migItem.step[step] = 'update';
+ break;
+
+ case 'duplicate':
+ if (DOM.duplicateEntryWithChange(qEl, processOptions))
+ migItem.step[step] = 'duplicate';
+ break;
+ }
+ });
+ });
+
+ await FileIO.saveFile(doc.documentElement.outerHTML, path);
+}
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/rootPath.ts b/packages/SystemUI/scripts/token_alignment/helpers/rootPath.ts
new file mode 100644
index 0000000..2c6f632
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/rootPath.ts
@@ -0,0 +1,21 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.import { exec } from 'child_process';
+
+if (!process?.env?.ANDROID_BUILD_TOP) {
+ console.log(
+ "Error: Couldn't find 'ANDROID_BUILD_TOP' environment variable. Make sure to run 'lunch' in this terminal"
+ );
+}
+
+export const repoPath = process?.env?.ANDROID_BUILD_TOP;
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/textFuncs.ts b/packages/SystemUI/scripts/token_alignment/helpers/textFuncs.ts
new file mode 100644
index 0000000..6679c5a
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/textFuncs.ts
@@ -0,0 +1,27 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.import { exec } from 'child_process';
+
+export function groupReplace(src: string, replaceMap: Map<string, string>, pattern: string) {
+ const fullPattern = pattern.replace('#group#', [...replaceMap.keys()].join('|'));
+
+ const regEx = new RegExp(fullPattern, 'g');
+
+ ''.replace;
+
+ return src.replace(regEx, (...args) => {
+ //match, ...matches, offset, string, groups
+ const [match, key] = args as string[];
+ return match.replace(key, replaceMap.get(key) || '');
+ });
+}
diff --git a/packages/SystemUI/scripts/token_alignment/index.ts b/packages/SystemUI/scripts/token_alignment/index.ts
new file mode 100644
index 0000000..1b15e48
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/index.ts
@@ -0,0 +1,240 @@
+// Copyright 2022 Google LLC
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.import { exec } from 'child_process';
+
+import DOM from './helpers/DOMFuncs';
+import { FileIO } from './helpers/FileIO';
+import { loadMIgrationList } from './helpers/migrationList';
+import { processQueriedEntries, TEvalExistingEntry } from './helpers/processXML';
+import { repoPath } from './helpers/rootPath';
+import { groupReplace } from './helpers/textFuncs';
+
+async function init() {
+ const migrationMap = await loadMIgrationList();
+ const basePath = `${repoPath}/../tm-qpr-dev/frameworks/base/core/res/res/values/`;
+
+ await processQueriedEntries(migrationMap, {
+ containerQuery: 'declare-styleable[name="Theme"]',
+ hidable: true,
+ path: `${basePath}attrs.xml`,
+ step: 0,
+ tagName: 'attr',
+ evalExistingEntry: (_attrValue, migItem, qItem) => {
+ const { hidden, textContent: currentComment } = DOM.getElementComment(qItem);
+
+ if (hidden) migItem.isHidden = hidden;
+
+ const { newComment } = migItem;
+ return [
+ hidden ? 'update' : 'duplicate',
+ {
+ attrs: { name: migItem.replaceToken },
+ ...(newComment
+ ? { comment: `${newComment} @hide ` }
+ : currentComment
+ ? { comment: hidden ? currentComment : `${currentComment} @hide ` }
+ : {}),
+ },
+ ];
+ },
+ evalMissingEntry: (_originalToken, { replaceToken, newComment }) => {
+ return {
+ tagName: 'attr',
+ attrs: {
+ name: replaceToken,
+ format: 'color',
+ },
+ comment: `${newComment} @hide `,
+ };
+ },
+ });
+
+ // only update all existing entries
+ await processQueriedEntries(migrationMap, {
+ tagName: 'item',
+ path: `${basePath}themes_device_defaults.xml`,
+ containerQuery: 'resources',
+ step: 2,
+ evalExistingEntry: (_attrValue, { isHidden, replaceToken, step }, _qItem) => {
+ if (step[0] != 'ignore')
+ return [
+ isHidden ? 'update' : 'duplicate',
+ {
+ attrs: { name: replaceToken },
+ },
+ ];
+ },
+ });
+
+ // add missing entries on specific container
+ await processQueriedEntries(migrationMap, {
+ tagName: 'item',
+ path: `${basePath}themes_device_defaults.xml`,
+ containerQuery: 'resources style[parent="Theme.Material"]',
+ step: 3,
+ evalMissingEntry: (originalToken, { newDefaultValue, replaceToken }) => {
+ return {
+ tagName: 'item',
+ content: newDefaultValue,
+ attrs: {
+ name: replaceToken,
+ },
+ };
+ },
+ });
+
+ const evalExistingEntry: TEvalExistingEntry = (_attrValue, { replaceToken, step }, _qItem) => {
+ if (step[0] == 'update')
+ return [
+ 'update',
+ {
+ attrs: { name: replaceToken },
+ },
+ ];
+ };
+
+ await processQueriedEntries(migrationMap, {
+ tagName: 'item',
+ containerQuery: 'resources',
+ path: `${basePath}../values-night/themes_device_defaults.xml`,
+ step: 4,
+ evalExistingEntry,
+ });
+
+ await processQueriedEntries(migrationMap, {
+ tagName: 'java-symbol',
+ path: `${basePath}symbols.xml`,
+ containerQuery: 'resources',
+ step: 5,
+ evalExistingEntry,
+ });
+
+ // update attributes on tracked XML files
+ {
+ const searchAttrs = [
+ 'android:color',
+ 'android:indeterminateTint',
+ 'app:tint',
+ 'app:backgroundTint',
+ 'android:background',
+ 'android:tint',
+ 'android:drawableTint',
+ 'android:textColor',
+ 'android:fillColor',
+ 'android:startColor',
+ 'android:endColor',
+ 'name',
+ 'ns1:color',
+ ];
+
+ const filtered = new Map(
+ [...migrationMap]
+ .filter(([_originalToken, { step }]) => step[0] == 'update')
+ .map(([originalToken, { replaceToken }]) => [originalToken, replaceToken])
+ );
+
+ const query =
+ searchAttrs.map((str) => `*[${str}]`).join(',') +
+ [...filtered.keys()].map((originalToken) => `item[name*="${originalToken}"]`).join(',');
+
+ const trackedFiles = await FileIO.loadFileList(
+ `${__dirname}/resources/whitelist/xmls1.json`
+ );
+
+ const promises = trackedFiles.map(async (locaFilePath) => {
+ const filePath = `${repoPath}/${locaFilePath}`;
+
+ const doc = await FileIO.loadXML(filePath);
+ const docUpdated = DOM.replaceStringInAttributeValueOnQueried(
+ doc.documentElement,
+ query,
+ searchAttrs,
+ filtered
+ );
+ if (docUpdated) {
+ await FileIO.saveFile(DOM.XMLDocToString(doc), filePath);
+ } else {
+ console.warn(`Failed to update tracked file: '${locaFilePath}'`);
+ }
+ });
+ await Promise.all(promises);
+ }
+
+ // updates tag content on tracked files
+ {
+ const searchPrefixes = ['?android:attr/', '?androidprv:attr/'];
+ const filtered = searchPrefixes
+ .reduce<Array<[string, string]>>((acc, prefix) => {
+ return [
+ ...acc,
+ ...[...migrationMap.entries()]
+ .filter(([_originalToken, { step }]) => step[0] == 'update')
+ .map(
+ ([originalToken, { replaceToken }]) =>
+ [`${prefix}${originalToken}`, `${prefix}${replaceToken}`] as [
+ string,
+ string
+ ]
+ ),
+ ];
+ }, [])
+ .sort((a, b) => b[0].length - a[0].length);
+
+ const trackedFiles = await FileIO.loadFileList(
+ `${__dirname}/resources/whitelist/xmls2.json`
+ );
+
+ const promises = trackedFiles.map(async (locaFilePath) => {
+ const filePath = `${repoPath}/${locaFilePath}`;
+ const doc = await FileIO.loadXML(filePath);
+ const docUpdated = DOM.replaceContentTextOnQueried(
+ doc.documentElement,
+ 'item, color',
+ filtered
+ );
+ if (docUpdated) {
+ await FileIO.saveFile(DOM.XMLDocToString(doc), filePath);
+ } else {
+ console.warn(`Failed to update tracked file: '${locaFilePath}'`);
+ }
+ });
+ await Promise.all(promises);
+ }
+
+ // replace imports on Java / Kotlin
+ {
+ const replaceMap = new Map(
+ [...migrationMap.entries()]
+ .filter(([_originalToken, { step }]) => step[0] == 'update')
+ .map(
+ ([originalToken, { replaceToken }]) =>
+ [originalToken, replaceToken] as [string, string]
+ )
+ .sort((a, b) => b[0].length - a[0].length)
+ );
+
+ const trackedFiles = await FileIO.loadFileList(
+ `${__dirname}/resources/whitelist/java.json`
+ );
+
+ const promises = trackedFiles.map(async (locaFilePath) => {
+ const filePath = `${repoPath}/${locaFilePath}`;
+ const fileContent = await FileIO.loadFileAsText(filePath);
+ const str = groupReplace(fileContent, replaceMap, 'R.attr.(#group#)(?![a-zA-Z])');
+ await FileIO.saveFile(str, filePath);
+ });
+ await Promise.all(promises);
+ }
+}
+
+init();
diff --git a/packages/SystemUI/scripts/token_alignment/package-lock.json b/packages/SystemUI/scripts/token_alignment/package-lock.json
new file mode 100644
index 0000000..da9edb3
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/package-lock.json
@@ -0,0 +1,3356 @@
+{
+ "name": "token_alignment",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "csv-parse": "^5.3.3",
+ "high5": "^1.0.0",
+ "jsdom": "^20.0.3"
+ },
+ "devDependencies": {
+ "@types/jsdom": "^20.0.1",
+ "@types/node": "^18.11.18",
+ "@typescript-eslint/eslint-plugin": "^5.48.0",
+ "eslint-config-prettier": "^8.6.0",
+ "eslint-plugin-import": "^2.26.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "eslint-plugin-simple-import-sort": "^8.0.0",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.4"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
+ "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.4.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+ "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "dev": true
+ },
+ "node_modules/@types/jsdom": {
+ "version": "20.0.1",
+ "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
+ "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "@types/tough-cookie": "*",
+ "parse5": "^7.0.0"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+ "dev": true
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "18.11.18",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
+ "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
+ "dev": true
+ },
+ "node_modules/@types/semver": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+ "dev": true
+ },
+ "node_modules/@types/tough-cookie": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz",
+ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==",
+ "dev": true
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.0.tgz",
+ "integrity": "sha512-SVLafp0NXpoJY7ut6VFVUU9I+YeFsDzeQwtK0WZ+xbRN3mtxJ08je+6Oi2N89qDn087COdO0u3blKZNv9VetRQ==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.48.0",
+ "@typescript-eslint/type-utils": "5.48.0",
+ "@typescript-eslint/utils": "5.48.0",
+ "debug": "^4.3.4",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "regexpp": "^3.2.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^5.0.0",
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.0.tgz",
+ "integrity": "sha512-1mxNA8qfgxX8kBvRDIHEzrRGrKHQfQlbW6iHyfHYS0Q4X1af+S6mkLNtgCOsGVl8+/LUPrqdHMssAemkrQ01qg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.48.0",
+ "@typescript-eslint/types": "5.48.0",
+ "@typescript-eslint/typescript-estree": "5.48.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.0.tgz",
+ "integrity": "sha512-0AA4LviDtVtZqlyUQnZMVHydDATpD9SAX/RC5qh6cBd3xmyWvmXYF+WT1oOmxkeMnWDlUVTwdODeucUnjz3gow==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.48.0",
+ "@typescript-eslint/visitor-keys": "5.48.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.0.tgz",
+ "integrity": "sha512-vbtPO5sJyFjtHkGlGK4Sthmta0Bbls4Onv0bEqOGm7hP9h8UpRsHJwsrCiWtCUndTRNQO/qe6Ijz9rnT/DB+7g==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "5.48.0",
+ "@typescript-eslint/utils": "5.48.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.0.tgz",
+ "integrity": "sha512-UTe67B0Ypius0fnEE518NB2N8gGutIlTojeTg4nt0GQvikReVkurqxd2LvYa9q9M5MQ6rtpNyWTBxdscw40Xhw==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.0.tgz",
+ "integrity": "sha512-7pjd94vvIjI1zTz6aq/5wwE/YrfIyEPLtGJmRfyNR9NYIW+rOvzzUv3Cmq2hRKpvt6e9vpvPUQ7puzX7VSmsEw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.48.0",
+ "@typescript-eslint/visitor-keys": "5.48.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.0.tgz",
+ "integrity": "sha512-x2jrMcPaMfsHRRIkL+x96++xdzvrdBCnYRd5QiW5Wgo1OB4kDYPbC1XjWP/TNqlfK93K/lUL92erq5zPLgFScQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.48.0",
+ "@typescript-eslint/types": "5.48.0",
+ "@typescript-eslint/typescript-estree": "5.48.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.0.tgz",
+ "integrity": "sha512-5motVPz5EgxQ0bHjut3chzBkJ3Z3sheYVcSwS5BpHZpLqSptSmELNtGixmgj65+rIfhvtQTz5i9OP2vtzdDH7Q==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.48.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
+ },
+ "node_modules/acorn": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+ "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-globals": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
+ "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
+ "dependencies": {
+ "acorn": "^8.1.0",
+ "acorn-walk": "^8.0.2"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peer": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
+ "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4",
+ "get-intrinsic": "^1.1.3",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
+ "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cssom": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
+ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="
+ },
+ "node_modules/cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dependencies": {
+ "cssom": "~0.3.6"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cssstyle/node_modules/cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
+ },
+ "node_modules/csv-parse": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.3.3.tgz",
+ "integrity": "sha512-kEWkAPleNEdhFNkHQpFHu9RYPogsFj3dx6bCxL847fsiLgidzWg0z/O0B1kVWMJUc5ky64zGp18LX2T3DQrOfw=="
+ },
+ "node_modules/data-urls": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
+ "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
+ "dependencies": {
+ "abab": "^2.0.6",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+ },
+ "node_modules/define-properties": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+ "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+ "dev": true,
+ "dependencies": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/domexception": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
+ "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+ "dependencies": {
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.0.tgz",
+ "integrity": "sha512-GUGtW7eXQay0c+PRq0sGIKSdaBorfVqsCMhGHo4elP7YVqZu9nCZS4UkK4gv71gOWNMra/PaSKD3ao1oWExO0g==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "es-set-tostringtag": "^2.0.0",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.1.3",
+ "get-symbol-description": "^1.0.0",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.4",
+ "is-array-buffer": "^3.0.0",
+ "is-callable": "^1.2.7",
+ "is-negative-zero": "^2.0.2",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.10",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.12.2",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.4.3",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trimend": "^1.0.6",
+ "string.prototype.trimstart": "^1.0.6",
+ "typed-array-length": "^1.0.4",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+ "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3",
+ "has": "^1.0.3",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+ "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+ "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.31.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz",
+ "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@eslint/eslintrc": "^1.4.1",
+ "@humanwhocodes/config-array": "^0.11.8",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.1.1",
+ "eslint-utils": "^3.0.0",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.4.0",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "grapheme-splitter": "^1.0.4",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-sdsl": "^4.1.4",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "regexpp": "^3.2.0",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
+ "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz",
+ "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "resolve": "^1.20.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+ "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.26.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
+ "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.4",
+ "array.prototype.flat": "^1.2.5",
+ "debug": "^2.6.9",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-module-utils": "^2.7.3",
+ "has": "^1.0.3",
+ "is-core-module": "^2.8.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.values": "^1.1.5",
+ "resolve": "^1.22.0",
+ "tsconfig-paths": "^3.14.1"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+ "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.28.0",
+ "prettier": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-simple-import-sort": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz",
+ "integrity": "sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw==",
+ "dev": true,
+ "peerDependencies": {
+ "eslint": ">=5.0.0"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+ "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+ "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+ "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+ "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "acorn": "^8.8.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+ "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.0",
+ "functions-have-names": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+ "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.19.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
+ "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+ "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/high5": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/high5/-/high5-1.0.0.tgz",
+ "integrity": "sha512-xucW/5M1hd+p6bj530wtRSKwqUQrgiIgOWepi4Di9abkonZaxhTDf0zrqqraxfZSXBcFSuc1/WVGBIlqSe1Hdw==",
+ "dependencies": {
+ "entities": "1.0"
+ }
+ },
+ "node_modules/high5/node_modules/entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ=="
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
+ "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+ "dependencies": {
+ "whatwg-encoding": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/internal-slot": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
+ "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz",
+ "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-typed-array": "^1.1.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
+ "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/js-sdsl": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
+ "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
+ "dev": true,
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/js-sdsl"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
+ "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
+ "dependencies": {
+ "abab": "^2.0.6",
+ "acorn": "^8.8.1",
+ "acorn-globals": "^7.0.0",
+ "cssom": "^0.5.0",
+ "cssstyle": "^2.3.0",
+ "data-urls": "^3.0.2",
+ "decimal.js": "^10.4.2",
+ "domexception": "^4.0.0",
+ "escodegen": "^2.0.0",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^3.0.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.1",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.2",
+ "parse5": "^7.1.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.1.2",
+ "w3c-xmlserializer": "^4.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^2.0.0",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0",
+ "ws": "^8.11.0",
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/natural-compare-lite": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+ "dev": true
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
+ "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw=="
+ },
+ "node_modules/object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
+ "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+ "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+ "dependencies": {
+ "entities": "^4.4.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.2.tgz",
+ "integrity": "sha512-BtRV9BcncDyI2tsuS19zzhzoxD8Dh8LiCx7j7tHzrkz8GFXAexeWFdi22mjE1d16dftH2qNaytVxqiRTGlMfpw==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+ "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "functions-have-names": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
+ },
+ "node_modules/resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+ "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
+ "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
+ "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
+ "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "dev": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+ "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+ "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "is-typed-array": "^1.1.9"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.9.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
+ "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
+ "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
+ "dependencies": {
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
+ "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
+ "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+ "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+ "dependencies": {
+ "tr46": "^3.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
+ "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.0",
+ "is-typed-array": "^1.1.10"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/ws": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/package.json b/packages/SystemUI/scripts/token_alignment/package.json
new file mode 100644
index 0000000..2e63668
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/package.json
@@ -0,0 +1,22 @@
+{
+ "dependencies": {
+ "csv-parse": "^5.3.3",
+ "high5": "^1.0.0",
+ "jsdom": "^20.0.3"
+ },
+ "devDependencies": {
+ "@types/jsdom": "^20.0.1",
+ "@types/node": "^18.11.18",
+ "@typescript-eslint/eslint-plugin": "^5.48.0",
+ "eslint-config-prettier": "^8.6.0",
+ "eslint-plugin-import": "^2.26.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "eslint-plugin-simple-import-sort": "^8.0.0",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.4"
+ },
+ "scripts": {
+ "main": "ts-node ./index.ts",
+ "resetRepo": "repo forall -j100 -c 'git restore .'"
+ }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/resources/migrationList.csv b/packages/SystemUI/scripts/token_alignment/resources/migrationList.csv
new file mode 100644
index 0000000..4111bb3
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/migrationList.csv
@@ -0,0 +1,16 @@
+android material newDefaultValue newComment migrationToken
+colorAccentPrimaryVariant colorPrimaryContainer MigTok02
+colorAccentSecondaryVariant colorSecondaryContainer MigTok04
+colorAccentTertiary colorTertiary MigTok05
+colorAccentTertiaryVariant colorTertiaryContainer MigTok06
+colorBackground colorSurfaceContainer MigTok07
+colorSurface colorSurfaceContainer MigTok08
+colorSurfaceHeader colorSurfaceContainerHighest MigTok09
+colorSurfaceHighlight colorSurfaceBright MigTok10
+colorSurfaceVariant colorSurfaceContainerHigh MigTok11
+textColorOnAccent colorOnPrimary MigTok12
+textColorPrimary colorOnSurface MigTok13
+textColorPrimaryInverse colorOnShadeInactive MigTok14
+textColorSecondary colorOnSurfaceVariant MigTok15
+textColorSecondaryInverse colorOnShadeInactiveVariant MigTok16
+textColorTertiary colorOutline MigTok17
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/resources/whitelist/java.json b/packages/SystemUI/scripts/token_alignment/resources/whitelist/java.json
new file mode 100644
index 0000000..7f55b2d
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/whitelist/java.json
@@ -0,0 +1,30 @@
+[
+ "frameworks/base/core/java/android/app/Notification.java",
+ "packages/apps/Settings/src/com/android/settings/dashboard/profileselector/UserAdapter.java",
+ "packages/apps/Settings/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java",
+ "frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt",
+ "frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt",
+ "packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/PreviewFragment.java",
+ "packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/CategorySelectorFragment.java",
+ "packages/apps/Launcher3/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt",
+ "frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java",
+ "vendor/unbundled_google/packages/NexusLauncher/src/com/google/android/apps/nexuslauncher/customize/WallpaperCarouselView.java",
+ "vendor/unbundled_google/packages/NexusLauncher/src/com/google/android/apps/nexuslauncher/quickstep/TaskOverlayFactoryImpl.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt",
+ "frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java",
+ "frameworks/base/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java",
+ "frameworks/base/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java"
+]
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls1.json b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls1.json
new file mode 100644
index 0000000..1e59773
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls1.json
@@ -0,0 +1,184 @@
+[
+ "frameworks/base/core/res/res/color/resolver_profile_tab_selected_bg.xml",
+ "frameworks/base/core/res/res/color-night/resolver_profile_tab_selected_bg.xml",
+ "frameworks/base/core/res/res/drawable/autofill_bottomsheet_background.xml",
+ "frameworks/base/core/res/res/drawable/btn_outlined.xml",
+ "frameworks/base/core/res/res/drawable/btn_tonal.xml",
+ "frameworks/base/core/res/res/drawable/chooser_action_button_bg.xml",
+ "frameworks/base/core/res/res/drawable/chooser_row_layer_list.xml",
+ "frameworks/base/core/res/res/drawable/resolver_outlined_button_bg.xml",
+ "frameworks/base/core/res/res/drawable/resolver_profile_tab_bg.xml",
+ "frameworks/base/core/res/res/drawable/toast_frame.xml",
+ "frameworks/base/core/res/res/drawable/work_widget_mask_view_background.xml",
+ "frameworks/base/core/res/res/layout/app_language_picker_current_locale_item.xml",
+ "frameworks/base/core/res/res/layout/app_language_picker_system_current.xml",
+ "frameworks/base/core/res/res/layout/autofill_save.xml",
+ "frameworks/base/core/res/res/layout/chooser_grid.xml",
+ "frameworks/base/core/res/res/layout/user_switching_dialog.xml",
+ "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml",
+ "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml",
+ "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml",
+ "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml",
+ "packages/modules/IntentResolver/java/res/drawable/chooser_action_button_bg.xml",
+ "packages/modules/IntentResolver/java/res/drawable/chooser_row_layer_list.xml",
+ "packages/modules/IntentResolver/java/res/drawable/resolver_outlined_button_bg.xml",
+ "packages/modules/IntentResolver/java/res/drawable/resolver_profile_tab_bg.xml",
+ "packages/modules/IntentResolver/java/res/layout/chooser_grid.xml",
+ "frameworks/base/libs/WindowManager/Shell/res/color/one_handed_tutorial_background_color.xml",
+ "frameworks/base/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml",
+ "frameworks/base/libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg.xml",
+ "frameworks/base/libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg_rtl.xml",
+ "vendor/unbundled_google/packages/SystemUIGoogle/bcsmartspace/res/drawable/bg_smartspace_combination_sub_card.xml",
+ "vendor/unbundled_google/packages/SystemUIGoogle/bcsmartspace/res/layout/smartspace_combination_sub_card.xml",
+ "packages/apps/Launcher3/res/drawable/bg_rounded_corner_bottom_sheet_handle.xml",
+ "packages/apps/Launcher3/res/drawable/rounded_action_button.xml",
+ "packages/apps/Launcher3/res/drawable/work_card.xml",
+ "packages/apps/Nfc/res/color/nfc_icon.xml",
+ "packages/apps/Nfc/res/color-night/nfc_icon.xml",
+ "packages/apps/Launcher3/quickstep/res/drawable/bg_overview_clear_all_button.xml",
+ "packages/apps/Launcher3/quickstep/res/drawable/bg_sandbox_feedback.xml",
+ "packages/apps/Launcher3/quickstep/res/drawable/bg_wellbeing_toast.xml",
+ "packages/apps/Launcher3/quickstep/res/drawable/button_taskbar_edu_bordered.xml",
+ "packages/apps/Launcher3/quickstep/res/drawable/button_taskbar_edu_colored.xml",
+ "packages/apps/Launcher3/quickstep/res/drawable/split_instructions_background.xml",
+ "packages/apps/Launcher3/quickstep/res/drawable/task_menu_item_bg.xml",
+ "packages/apps/Launcher3/quickstep/res/layout/digital_wellbeing_toast.xml",
+ "packages/apps/Launcher3/quickstep/res/layout/split_instructions_view.xml",
+ "packages/apps/Launcher3/quickstep/res/layout/taskbar_edu.xml",
+ "frameworks/base/packages/SettingsLib/res/drawable/broadcast_dialog_btn_bg.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color/share_target_text.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/all_apps_tab_background_selected.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/all_apps_tabs_background.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/arrow_tip_view_bg.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/button_bg.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/shortcut_halo.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/surface.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-night-v31/all_apps_tab_background_selected.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/color-night-v31/surface.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/bg_pin_keyboard_snackbar_accept_button.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/bg_search_edu_preferences_button.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/circle_accentprimary_32dp.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/ic_search.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/ic_suggest_icon_background.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/share_target_background.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/sticky_snackbar_accept_btn_background.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/sticky_snackbar_background.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/sticky_snackbar_dismiss_btn_background.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/drawable/tall_card_btn_background.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/layout/section_header.xml",
+ "packages/apps/Settings/res/color/dream_card_color_state_list.xml",
+ "packages/apps/Settings/res/color/dream_card_icon_color_state_list.xml",
+ "packages/apps/Settings/res/color/dream_card_text_color_state_list.xml",
+ "packages/apps/Settings/res/drawable/accessibility_text_reading_preview.xml",
+ "packages/apps/Settings/res/drawable/broadcast_button_outline.xml",
+ "packages/apps/Settings/res/drawable/button_border_selected.xml",
+ "packages/apps/Settings/res/drawable/dream_preview_rounded_bg.xml",
+ "packages/apps/Settings/res/drawable/rounded_bg.xml",
+ "packages/apps/Settings/res/drawable/sim_confirm_dialog_btn_outline.xml",
+ "packages/apps/Settings/res/drawable/user_select_background.xml",
+ "packages/apps/Settings/res/drawable/volume_dialog_button_background_outline.xml",
+ "packages/apps/Settings/res/drawable/volume_dialog_button_background_solid.xml",
+ "packages/apps/Settings/res/layout/dream_preview_button.xml",
+ "packages/apps/Settings/res/layout/qrcode_scanner_fragment.xml",
+ "frameworks/base/packages/SystemUI/res/values-television/styles.xml",
+ "frameworks/base/packages/SystemUI/res/color/media_player_album_bg.xml",
+ "frameworks/base/packages/SystemUI/res/color/media_player_outline_button_bg.xml",
+ "frameworks/base/packages/SystemUI/res/color/media_player_solid_button_bg.xml",
+ "frameworks/base/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml",
+ "frameworks/base/packages/SystemUI/res/color/settingslib_state_on.xml",
+ "frameworks/base/packages/SystemUI/res/color/settingslib_track_off.xml",
+ "frameworks/base/packages/SystemUI/res/color/settingslib_track_on.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/action_chip_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/action_chip_container_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/availability_dot_10dp.xml",
+ "frameworks/base/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_header_bg.xml",
+ "frameworks/base/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_item_selected_bg.xml",
+ "frameworks/base/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_popup_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/fgs_dot.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/fingerprint_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/ic_avatar_with_badge.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml",
+ "frameworks/base/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml",
+ "frameworks/base/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/logout_button_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/media_ttt_chip_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/media_ttt_chip_background_receiver.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/media_ttt_undo_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/notif_footer_btn_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/notification_guts_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/overlay_badge_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/overlay_border.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/overlay_button_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/overlay_cancel.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/people_space_messages_count_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/people_tile_status_scrim.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/qs_media_outline_button.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/qs_media_solid_button.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/rounded_bg_full.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/screenrecord_button_background_solid.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/screenrecord_spinner_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/screenshot_edit_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/volume_background_bottom.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/volume_background_top.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/volume_background_top_rounded.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/volume_row_rounded_background.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/volume_row_seekbar.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/wallet_action_button_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/wallet_app_button_bg.xml",
+ "frameworks/base/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml",
+ "frameworks/base/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml",
+ "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+ "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+ "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay.xml",
+ "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay.xml",
+ "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml",
+ "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml",
+ "frameworks/base/packages/SystemUI/res/layout/internet_connectivity_dialog.xml",
+ "frameworks/base/packages/SystemUI/res/layout/notification_snooze.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_space_tile_view.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_large_with_content.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_medium_with_content.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml",
+ "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+ "frameworks/base/packages/SystemUI/res/layout/notification_snooze.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_small.xml",
+ "frameworks/base/packages/SystemUI/res/layout/people_tile_small_horizontal.xml",
+ "frameworks/base/packages/SystemUI/res/layout/screen_share_dialog.xml",
+ "frameworks/base/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml",
+ "frameworks/base/packages/SystemUI/res/layout/user_switcher_fullscreen.xml",
+ "frameworks/base/packages/SystemUI/res/layout/user_switcher_fullscreen.xml",
+ "frameworks/base/packages/SystemUI/res/layout/wallet_empty_state.xml",
+ "frameworks/base/packages/SystemUI/res/layout/wallet_fullscreen.xml",
+ "frameworks/base/packages/SystemUI/res/layout/wallet_fullscreen.xml",
+ "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+ "frameworks/base/packages/SystemUI/res/layout/notification_snooze.xml",
+ "vendor/unbundled_google/packages/SystemUIGoogle/res/drawable/columbus_chip_background_raw.xml",
+ "vendor/unbundled_google/packages/SystemUIGoogle/res/drawable/columbus_chip_background_raw.xml",
+ "vendor/unbundled_google/packages/SystemUIGoogle/res/drawable/columbus_dialog_background.xml",
+ "vendor/unbundled_google/packages/SystemUIGoogle/res/layout/columbus_target_request_dialog.xml",
+ "vendor/unbundled_google/packages/SettingsGoogle/res/color/dream_card_suw_color_state_list.xml",
+ "vendor/unbundled_google/packages/SettingsGoogle/res/drawable/dream_item_suw_rounded_bg.xml"
+]
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls2.json b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls2.json
new file mode 100644
index 0000000..20a7c76
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls2.json
@@ -0,0 +1,13 @@
+[
+ "vendor/google/nexus_overlay/PixelDocumentsUIGoogleOverlay/res/values-v31/themes.xml",
+ "vendor/google/nexus_overlay/PixelDocumentsUIGoogleOverlay/res/values-night-v31/themes.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/values/colors.xml",
+ "vendor/unbundled_google/packages/NexusLauncher/res/values/styles.xml",
+ "packages/apps/Settings/res/values-night/colors.xml",
+ "packages/apps/Settings/res/values/colors.xml",
+ "packages/apps/Settings/res/values/styles.xml",
+ "frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml",
+ "frameworks/base/packages/SystemUI/res/values/styles.xml",
+ "vendor/unbundled_google/packages/SettingsGoogle/res/values/styles.xml",
+ "vendor/unbundled_google/packages/SettingsGoogle/res/values/styles.xml"
+]
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/tsconfig.json b/packages/SystemUI/scripts/token_alignment/tsconfig.json
new file mode 100644
index 0000000..20c7321
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/tsconfig.json
@@ -0,0 +1,103 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Speciffy the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ "typeRoots": ["../node_modules/@types"], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ "resolveJsonModule": true, /* Enable importing .json files. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
index 0ee813b..ef2247f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
@@ -15,39 +15,36 @@
*/
package com.android.systemui.shared.regionsampling
+import android.app.WallpaperColors
+import android.app.WallpaperManager
import android.graphics.Color
+import android.graphics.Point
import android.graphics.Rect
+import android.graphics.RectF
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.systemui.shared.navigationbar.RegionSamplingHelper
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper.SamplingCallback
import java.io.PrintWriter
import java.util.concurrent.Executor
/** Class for instance of RegionSamplingHelper */
-open class RegionSampler(
- sampledView: View?,
+open class RegionSampler
+@JvmOverloads
+constructor(
+ val sampledView: View?,
mainExecutor: Executor?,
- bgExecutor: Executor?,
- regionSamplingEnabled: Boolean,
- updateFun: UpdateColorCallback
-) {
+ val bgExecutor: Executor?,
+ val regionSamplingEnabled: Boolean,
+ val updateForegroundColor: UpdateColorCallback,
+ val wallpaperManager: WallpaperManager? = WallpaperManager.getInstance(sampledView?.context)
+) : WallpaperManager.LocalWallpaperColorConsumer {
private var regionDarkness = RegionDarkness.DEFAULT
private var samplingBounds = Rect()
private val tmpScreenLocation = IntArray(2)
@VisibleForTesting var regionSampler: RegionSamplingHelper? = null
private var lightForegroundColor = Color.WHITE
private var darkForegroundColor = Color.BLACK
-
- @VisibleForTesting
- open fun createRegionSamplingHelper(
- sampledView: View,
- callback: SamplingCallback,
- mainExecutor: Executor?,
- bgExecutor: Executor?
- ): RegionSamplingHelper {
- return RegionSamplingHelper(sampledView, callback, mainExecutor, bgExecutor)
- }
+ private val displaySize = Point()
/**
* Sets the colors to be used for Dark and Light Foreground.
@@ -73,7 +70,7 @@
}
}
- private fun convertToClockDarkness(isRegionDark: Boolean): RegionDarkness {
+ private fun getRegionDarkness(isRegionDark: Boolean): RegionDarkness {
return if (isRegionDark) {
RegionDarkness.DARK
} else {
@@ -87,12 +84,32 @@
/** Start region sampler */
fun startRegionSampler() {
- regionSampler?.start(samplingBounds)
+ if (!regionSamplingEnabled || sampledView == null) {
+ return
+ }
+
+ val sampledRegion = calculateSampledRegion(sampledView)
+ val regions = ArrayList<RectF>()
+ val sampledRegionWithOffset = convertBounds(sampledRegion)
+ regions.add(sampledRegionWithOffset)
+
+ wallpaperManager?.removeOnColorsChangedListener(this)
+ wallpaperManager?.addOnColorsChangedListener(this, regions)
+
+ // TODO(b/265969235): conditionally set FLAG_LOCK or FLAG_SYSTEM once HS smartspace
+ // implemented
+ bgExecutor?.execute(
+ Runnable {
+ val initialSampling =
+ wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK)
+ onColorsChanged(sampledRegionWithOffset, initialSampling)
+ }
+ )
}
/** Stop region sampler */
fun stopRegionSampler() {
- regionSampler?.stop()
+ wallpaperManager?.removeOnColorsChangedListener(this)
}
/** Dump region sampler */
@@ -100,43 +117,66 @@
regionSampler?.dump(pw)
}
- init {
- if (regionSamplingEnabled && sampledView != null) {
- regionSampler =
- createRegionSamplingHelper(
- sampledView,
- object : SamplingCallback {
- override fun onRegionDarknessChanged(isRegionDark: Boolean) {
- regionDarkness = convertToClockDarkness(isRegionDark)
- updateFun()
- }
- /**
- * The method getLocationOnScreen is used to obtain the view coordinates
- * relative to its left and top edges on the device screen. Directly
- * accessing the X and Y coordinates of the view returns the location
- * relative to its parent view instead.
- */
- override fun getSampledRegion(sampledView: View): Rect {
- val screenLocation = tmpScreenLocation
- sampledView.getLocationOnScreen(screenLocation)
- val left = screenLocation[0]
- val top = screenLocation[1]
- samplingBounds.left = left
- samplingBounds.top = top
- samplingBounds.right = left + sampledView.width
- samplingBounds.bottom = top + sampledView.height
- return samplingBounds
- }
+ fun calculateSampledRegion(sampledView: View): RectF {
+ val screenLocation = tmpScreenLocation
+ /**
+ * The method getLocationOnScreen is used to obtain the view coordinates relative to its
+ * left and top edges on the device screen. Directly accessing the X and Y coordinates of
+ * the view returns the location relative to its parent view instead.
+ */
+ sampledView.getLocationOnScreen(screenLocation)
+ val left = screenLocation[0]
+ val top = screenLocation[1]
- override fun isSamplingEnabled(): Boolean {
- return regionSamplingEnabled
- }
- },
- mainExecutor,
- bgExecutor
- )
- }
- regionSampler?.setWindowVisible(true)
+ samplingBounds.left = left
+ samplingBounds.top = top
+ samplingBounds.right = left + sampledView.width
+ samplingBounds.bottom = top + sampledView.height
+
+ return RectF(samplingBounds)
+ }
+
+ /**
+ * Convert the bounds of the region we want to sample from to fractional offsets because
+ * WallpaperManager requires the bounds to be between [0,1]. The wallpaper is treated as one
+ * continuous image, so if there are multiple screens, then each screen falls into a fractional
+ * range. For instance, 4 screens have the ranges [0, 0.25], [0,25, 0.5], [0.5, 0.75], [0.75,
+ * 1].
+ */
+ fun convertBounds(originalBounds: RectF): RectF {
+
+ // TODO(b/265969235): GRAB # PAGES + CURRENT WALLPAPER PAGE # FROM LAUNCHER
+ // TODO(b/265968912): remove hard-coded value once LS wallpaper supported
+ val wallpaperPageNum = 0
+ val numScreens = 1
+
+ val screenWidth = displaySize.x
+ // TODO: investigate small difference between this and the height reported in go/web-hv
+ val screenHeight = displaySize.y
+
+ val newBounds = RectF()
+ // horizontal
+ newBounds.left = ((originalBounds.left / screenWidth) + wallpaperPageNum) / numScreens
+ newBounds.right = ((originalBounds.right / screenWidth) + wallpaperPageNum) / numScreens
+ // vertical
+ newBounds.top = originalBounds.top / screenHeight
+ newBounds.bottom = originalBounds.bottom / screenHeight
+
+ return newBounds
+ }
+
+ init {
+ sampledView?.context?.display?.getSize(displaySize)
+ }
+
+ override fun onColorsChanged(area: RectF?, colors: WallpaperColors?) {
+ // update text color when wallpaper color changes
+ regionDarkness =
+ getRegionDarkness(
+ (colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
+ WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+ )
+ updateForegroundColor()
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 766266d..0d35aa3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -112,7 +112,8 @@
public static final int SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 25;
// Freeform windows are showing in desktop mode
public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
-
+ // Device dreaming state
+ public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -141,7 +142,8 @@
SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
SYSUI_STATE_IMMERSIVE_MODE,
SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
- SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE
+ SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
+ SYSUI_STATE_DEVICE_DREAMING
})
public @interface SystemUiStateFlags {}
@@ -179,6 +181,8 @@
str.add((flags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0 ? "vis_win_showing" : "");
str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0
? "freeform_active_in_desktop_mode" : "");
+ str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : "");
+
return str.toString();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index a45ce42..1680b47 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -24,6 +24,7 @@
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
+import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -47,18 +48,17 @@
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.Executor
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.launch
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -89,7 +89,11 @@
value.largeClock.logBuffer = largeLogBuffer
value.initialize(resources, dozeAmount, 0f)
- updateRegionSamplers(value)
+
+ if (regionSamplingEnabled) {
+ clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
+ clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
+ }
updateFontSizes()
}
}
@@ -104,47 +108,87 @@
private var disposableHandle: DisposableHandle? = null
private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
- private fun updateColors() {
+ private val mLayoutChangedListener = object : View.OnLayoutChangeListener {
+ private var currentSmallClockView: View? = null
+ private var currentLargeClockView: View? = null
+ private var currentSmallClockLocation = IntArray(2)
+ private var currentLargeClockLocation = IntArray(2)
- if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
- val wallpaperManager = WallpaperManager.getInstance(context)
- if (!wallpaperManager.lockScreenWallpaperExists()) {
- smallClockIsDark = smallRegionSampler!!.currentRegionDarkness().isDark
- largeClockIsDark = largeRegionSampler!!.currentRegionDarkness().isDark
- }
- } else {
- val isLightTheme = TypedValue()
- context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
- smallClockIsDark = isLightTheme.data == 0
- largeClockIsDark = isLightTheme.data == 0
+ override fun onLayoutChange(
+ view: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val parent = (view?.parent) as FrameLayout
+
+ // don't pass in negative bounds when clocks are in transition state
+ if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
+ return
}
+ // SMALL CLOCK
+ if (parent.id == R.id.lockscreen_clock_view) {
+ // view bounds have changed due to clock size changing (i.e. different character widths)
+ // AND/OR the view has been translated when transitioning between small and large clock
+ if (view != currentSmallClockView ||
+ !view.locationOnScreen.contentEquals(currentSmallClockLocation)) {
+ currentSmallClockView = view
+ currentSmallClockLocation = view.locationOnScreen
+ updateRegionSampler(view)
+ }
+ }
+ // LARGE CLOCK
+ else if (parent.id == R.id.lockscreen_clock_view_large) {
+ if (view != currentLargeClockView ||
+ !view.locationOnScreen.contentEquals(currentLargeClockLocation)) {
+ currentLargeClockView = view
+ currentLargeClockLocation = view.locationOnScreen
+ updateRegionSampler(view)
+ }
+ }
+ }
+ }
+
+ private fun updateColors() {
+ val wallpaperManager = WallpaperManager.getInstance(context)
+ if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) {
+ if (regionSampler != null) {
+ if (regionSampler?.sampledView == clock?.smallClock?.view) {
+ smallClockIsDark = regionSampler!!.currentRegionDarkness().isDark
+ clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
+ return
+ } else if (regionSampler?.sampledView == clock?.largeClock?.view) {
+ largeClockIsDark = regionSampler!!.currentRegionDarkness().isDark
+ clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
+ return
+ }
+ }
+ }
+
+ val isLightTheme = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
+ smallClockIsDark = isLightTheme.data == 0
+ largeClockIsDark = isLightTheme.data == 0
+
clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
}
- private fun updateRegionSamplers(currentClock: ClockController?) {
- smallRegionSampler?.stopRegionSampler()
- largeRegionSampler?.stopRegionSampler()
-
- smallRegionSampler = createRegionSampler(
- currentClock?.smallClock?.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- ::updateColors
- )
-
- largeRegionSampler = createRegionSampler(
- currentClock?.largeClock?.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- ::updateColors
- )
-
- smallRegionSampler!!.startRegionSampler()
- largeRegionSampler!!.startRegionSampler()
+ private fun updateRegionSampler(sampledRegion: View) {
+ regionSampler?.stopRegionSampler()
+ regionSampler = createRegionSampler(
+ sampledRegion,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ ::updateColors
+ )?.apply { startRegionSampler() }
updateColors()
}
@@ -155,7 +199,7 @@
bgExecutor: Executor?,
regionSamplingEnabled: Boolean,
updateColors: () -> Unit
- ): RegionSampler {
+ ): RegionSampler? {
return RegionSampler(
sampledView,
mainExecutor,
@@ -164,8 +208,7 @@
updateColors)
}
- var smallRegionSampler: RegionSampler? = null
- var largeRegionSampler: RegionSampler? = null
+ var regionSampler: RegionSampler? = null
private var smallClockIsDark = true
private var largeClockIsDark = true
@@ -232,8 +275,6 @@
configurationController.addCallback(configListener)
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
- smallRegionSampler?.startRegionSampler()
- largeRegionSampler?.startRegionSampler()
disposableHandle = parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForDozing(this)
@@ -258,8 +299,7 @@
configurationController.removeCallback(configListener)
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
- smallRegionSampler?.stopRegionSampler()
- largeRegionSampler?.stopRegionSampler()
+ regionSampler?.stopRegionSampler()
}
private fun updateFontSizes() {
@@ -275,8 +315,7 @@
fun dump(pw: PrintWriter) {
pw.println(this)
clock?.dump(pw)
- smallRegionSampler?.dump(pw)
- largeRegionSampler?.dump(pw)
+ regionSampler?.dump(pw)
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 6d9c5df..0bb5e25 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -88,7 +88,9 @@
private final ClockRegistry.ClockChangeListener mClockChangedListener;
private ViewGroup mStatusArea;
- // If set will replace keyguard_slice_view
+
+ // If the SMARTSPACE flag is set, keyguard_slice_view is replaced by the following views.
+ private View mWeatherView;
private View mSmartspaceView;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@@ -192,10 +194,17 @@
if (mSmartspaceController.isEnabled()) {
View ksv = mView.findViewById(R.id.keyguard_slice_view);
- int ksvIndex = mStatusArea.indexOfChild(ksv);
+ int viewIndex = mStatusArea.indexOfChild(ksv);
ksv.setVisibility(View.GONE);
- addSmartspaceView(ksvIndex);
+ // TODO(b/261757708): add content observer for the Settings toggle and add/remove
+ // weather according to the Settings.
+ if (mSmartspaceController.isDateWeatherDecoupled()) {
+ addWeatherView(viewIndex);
+ viewIndex += 1;
+ }
+
+ addSmartspaceView(viewIndex);
}
mSecureSettings.registerContentObserverForUser(
@@ -237,6 +246,18 @@
}
}
+ private void addWeatherView(int index) {
+ mWeatherView = mSmartspaceController.buildAndConnectWeatherView(mView);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ MATCH_PARENT, WRAP_CONTENT);
+ mStatusArea.addView(mWeatherView, index, lp);
+ int startPadding = getContext().getResources().getDimensionPixelSize(
+ R.dimen.below_clock_padding_start);
+ int endPadding = getContext().getResources().getDimensionPixelSize(
+ R.dimen.below_clock_padding_end);
+ mWeatherView.setPaddingRelative(startPadding, 0, endPadding, 0);
+ }
+
private void addSmartspaceView(int index) {
mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 02776a2..ec8fa92 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,8 +15,6 @@
*/
package com.android.keyguard;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import android.app.Presentation;
import android.content.Context;
import android.graphics.Color;
@@ -37,9 +35,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
+import com.android.systemui.settings.DisplayTracker;
import java.util.concurrent.Executor;
@@ -53,6 +53,7 @@
private MediaRouter mMediaRouter = null;
private final DisplayManager mDisplayService;
+ private final DisplayTracker mDisplayTracker;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final Context mContext;
@@ -62,46 +63,43 @@
private final SparseArray<Presentation> mPresentations = new SparseArray<>();
- private final DisplayManager.DisplayListener mDisplayListener =
- new DisplayManager.DisplayListener() {
+ private final DisplayTracker.Callback mDisplayCallback =
+ new DisplayTracker.Callback() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ Trace.beginSection(
+ "KeyguardDisplayManager#onDisplayAdded(displayId=" + displayId + ")");
+ final Display display = mDisplayService.getDisplay(displayId);
+ if (mShowing) {
+ updateNavigationBarVisibility(displayId, false /* navBarVisible */);
+ showPresentation(display);
+ }
+ Trace.endSection();
+ }
- @Override
- public void onDisplayAdded(int displayId) {
- Trace.beginSection(
- "KeyguardDisplayManager#onDisplayAdded(displayId=" + displayId + ")");
- final Display display = mDisplayService.getDisplay(displayId);
- if (mShowing) {
- updateNavigationBarVisibility(displayId, false /* navBarVisible */);
- showPresentation(display);
- }
- Trace.endSection();
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
-
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- Trace.beginSection(
- "KeyguardDisplayManager#onDisplayRemoved(displayId=" + displayId + ")");
- hidePresentation(displayId);
- Trace.endSection();
- }
- };
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ Trace.beginSection(
+ "KeyguardDisplayManager#onDisplayRemoved(displayId=" + displayId + ")");
+ hidePresentation(displayId);
+ Trace.endSection();
+ }
+ };
@Inject
public KeyguardDisplayManager(Context context,
Lazy<NavigationBarController> navigationBarControllerLazy,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
+ DisplayTracker displayTracker,
+ @Main Executor mainExecutor,
@UiBackground Executor uiBgExecutor) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
mDisplayService = mContext.getSystemService(DisplayManager.class);
- mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
+ mDisplayTracker = displayTracker;
+ mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
}
private boolean isKeyguardShowable(Display display) {
@@ -109,7 +107,7 @@
if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
return false;
}
- if (display.getDisplayId() == DEFAULT_DISPLAY) {
+ if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) {
if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
return false;
}
@@ -224,7 +222,7 @@
protected boolean updateDisplays(boolean showing) {
boolean changed = false;
if (showing) {
- final Display[] displays = mDisplayService.getDisplays();
+ final Display[] displays = mDisplayTracker.getAllDisplays();
for (Display display : displays) {
int displayId = display.getDisplayId();
updateNavigationBarVisibility(displayId, false /* navBarVisible */);
@@ -247,7 +245,7 @@
// term solution in R.
private void updateNavigationBarVisibility(int displayId, boolean navBarVisible) {
// Leave this task to {@link StatusBarKeyguardViewManager}
- if (displayId == DEFAULT_DISPLAY) return;
+ if (displayId == mDisplayTracker.getDefaultDisplayId()) return;
NavigationBarView navBarView = mNavigationBarControllerLazy.get()
.getNavigationBarView(displayId);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index e4f85db..9f07a20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -92,6 +92,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.settingslib.Utils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -235,14 +236,6 @@
}
updateChildren(0 /* translationY */, 1f /* alpha */);
}
-
- private void updateChildren(int translationY, float alpha) {
- for (int i = 0; i < KeyguardSecurityContainer.this.getChildCount(); ++i) {
- View child = KeyguardSecurityContainer.this.getChildAt(i);
- child.setTranslationY(translationY);
- child.setAlpha(alpha);
- }
- }
};
private final OnBackAnimationCallback mBackCallback = new OnBackAnimationCallback() {
@@ -594,6 +587,7 @@
* This will run when the bouncer shows in all cases except when the user drags the bouncer up.
*/
public void startAppearAnimation(SecurityMode securityMode) {
+ updateChildren(0 /* translationY */, 1f /* alpha */);
mViewMode.startAppearAnimation(securityMode);
}
@@ -777,6 +771,14 @@
setScaleY(scale);
}
+ private void updateChildren(int translationY, float alpha) {
+ for (int i = 0; i < getChildCount(); ++i) {
+ View child = getChildAt(i);
+ child.setTranslationY(translationY);
+ child.setAlpha(alpha);
+ }
+ }
+
/**
* Enscapsulates the differences between bouncer modes for the container.
*/
@@ -998,8 +1000,10 @@
private Drawable findUserIcon(int userId) {
Bitmap userIcon = UserManager.get(mView.getContext()).getUserIcon(userId);
if (userIcon != null) {
- return new BitmapDrawable(userIcon);
+ return CircleFramedDrawable.getInstance(mView.getContext(),
+ userIcon);
}
+
return UserIcons.getDefaultUserIcon(mResources, userId, false);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 0b2b121..e3de8c7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -17,7 +17,6 @@
package com.android.keyguard;
import static android.app.slice.Slice.HINT_LIST_ITEM;
-import static android.view.Display.DEFAULT_DISPLAY;
import android.app.PendingIntent;
import android.net.Uri;
@@ -43,6 +42,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
@@ -64,6 +64,7 @@
private final ConfigurationController mConfigurationController;
private final TunerService mTunerService;
private final DumpManager mDumpManager;
+ private final DisplayTracker mDisplayTracker;
private int mDisplayId;
private LiveData<Slice> mLiveData;
private Uri mKeyguardSliceUri;
@@ -108,12 +109,14 @@
ActivityStarter activityStarter,
ConfigurationController configurationController,
TunerService tunerService,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ DisplayTracker displayTracker) {
super(keyguardSliceView);
mActivityStarter = activityStarter;
mConfigurationController = configurationController;
mTunerService = tunerService;
mDumpManager = dumpManager;
+ mDisplayTracker = displayTracker;
}
@Override
@@ -124,7 +127,7 @@
}
mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
// Make sure we always have the most current slice
- if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
+ if (mDisplayId == mDisplayTracker.getDefaultDisplayId() && mLiveData != null) {
mLiveData.observeForever(mObserver);
}
mConfigurationController.addCallback(mConfigurationListener);
@@ -137,7 +140,7 @@
@Override
protected void onViewDetached() {
// TODO(b/117344873) Remove below work around after this issue be fixed.
- if (mDisplayId == DEFAULT_DISPLAY) {
+ if (mDisplayId == mDisplayTracker.getDefaultDisplayId()) {
mLiveData.removeObserver(mObserver);
}
mTunerService.removeTunable(mTunable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9d6bb08..afa9ef6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -26,7 +26,6 @@
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
import static android.hardware.biometrics.BiometricConstants.LockoutMode;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -353,7 +352,6 @@
private final Executor mBackgroundExecutor;
private final SensorPrivacyManager mSensorPrivacyManager;
private final ActiveUnlockConfig mActiveUnlockConfig;
- private final PowerManager mPowerManager;
private final IDreamManager mDreamManager;
private final TelephonyManager mTelephonyManager;
@Nullable
@@ -361,7 +359,6 @@
@Nullable
private final FaceManager mFaceManager;
private final LockPatternUtils mLockPatternUtils;
- private final boolean mWakeOnFingerprintAcquiredStart;
@VisibleForTesting
@DevicePostureController.DevicePostureInt
protected int mConfigFaceAuthSupportedPosture;
@@ -885,11 +882,6 @@
private void handleFingerprintAcquired(
@BiometricFingerprintConstants.FingerprintAcquired int acquireInfo) {
Assert.isMainThread();
- if (mWakeOnFingerprintAcquiredStart && acquireInfo == FINGERPRINT_ACQUIRED_START) {
- mPowerManager.wakeUp(
- SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_BIOMETRIC,
- "com.android.systemui.keyguard:FINGERPRINT_ACQUIRED_START");
- }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -937,7 +929,7 @@
@Override
public void run() {
mLogger.logRetryAfterFpHwUnavailable(mHardwareFingerprintUnavailableRetryCount);
- if (mFpm.isHardwareDetected()) {
+ if (!mFingerprintSensorProperties.isEmpty()) {
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
mHardwareFingerprintUnavailableRetryCount++;
@@ -1538,6 +1530,7 @@
@VisibleForTesting
void setAssistantVisible(boolean assistantVisible) {
mAssistantVisible = assistantVisible;
+ mLogger.logAssistantVisible(mAssistantVisible);
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED);
if (mAssistantVisible) {
@@ -1945,6 +1938,11 @@
}
}
mGoingToSleep = true;
+ // Resetting assistant visibility state as the device is going to sleep now.
+ // TaskStackChangeListener gets triggered a little late when we transition to AoD,
+ // which results in face auth running once on AoD.
+ mAssistantVisible = false;
+ mLogger.d("Started going to sleep, mGoingToSleep=true, mAssistantVisible=false");
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_GOING_TO_SLEEP);
}
@@ -2050,7 +2048,6 @@
UiEventLogger uiEventLogger,
// This has to be a provider because SessionTracker depends on KeyguardUpdateMonitor :(
Provider<SessionTracker> sessionTrackerProvider,
- PowerManager powerManager,
TrustManager trustManager,
SubscriptionManager subscriptionManager,
UserManager userManager,
@@ -2087,7 +2084,6 @@
mLogger = logger;
mUiEventLogger = uiEventLogger;
mSessionTrackerProvider = sessionTrackerProvider;
- mPowerManager = powerManager;
mTrustManager = trustManager;
mUserManager = userManager;
mDreamManager = dreamManager;
@@ -2098,8 +2094,6 @@
mFpm = fingerprintManager;
mFaceManager = faceManager;
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
- mWakeOnFingerprintAcquiredStart = context.getResources()
- .getBoolean(com.android.internal.R.bool.kg_wake_on_acquire_start);
mFaceAcquiredInfoIgnoreList = Arrays.stream(
mContext.getResources().getIntArray(
R.array.config_face_acquire_device_entry_ignorelist))
@@ -2352,14 +2346,13 @@
}
private void updateFaceEnrolled(int userId) {
- mIsFaceEnrolled = whitelistIpcs(
- () -> mFaceManager != null && mFaceManager.isHardwareDetected()
- && mBiometricEnabledForUser.get(userId))
+ mIsFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
+ && mBiometricEnabledForUser.get(userId)
&& mAuthController.isFaceAuthEnrolled(userId);
}
public boolean isFaceSupported() {
- return mFaceManager != null && mFaceManager.isHardwareDetected();
+ return mFaceManager != null && !mFaceSensorProperties.isEmpty();
}
/**
@@ -2680,7 +2673,10 @@
private boolean shouldListenForFaceAssistant() {
BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
- return mAssistantVisible && mKeyguardOccluded
+ return mAssistantVisible
+ // There can be intermediate states where mKeyguardShowing is false but
+ // mKeyguardOccluded is true, we don't want to run face auth in such a scenario.
+ && (mKeyguardShowing && mKeyguardOccluded)
&& !(face != null && face.mAuthenticated)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
@@ -2975,7 +2971,8 @@
@VisibleForTesting
boolean isUnlockWithFingerprintPossible(int userId) {
// TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
- mIsUnlockWithFingerprintPossible.put(userId, mFpm != null && mFpm.isHardwareDetected()
+ mIsUnlockWithFingerprintPossible.put(userId, mFpm != null
+ && !mFingerprintSensorProperties.isEmpty()
&& !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
return mIsUnlockWithFingerprintPossible.get(userId);
}
@@ -3340,7 +3337,8 @@
/**
* Handle {@link #MSG_KEYGUARD_RESET}
*/
- private void handleKeyguardReset() {
+ @VisibleForTesting
+ protected void handleKeyguardReset() {
mLogger.d("handleKeyguardReset");
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_KEYGUARD_RESET);
@@ -3681,6 +3679,7 @@
if (info == null) {
return;
}
+ mLogger.logTaskStackChangedForAssistant(info.visible);
mHandler.sendMessage(mHandler.obtainMessage(MSG_ASSISTANT_STACK_CHANGED,
info.visible));
} catch (RemoteException e) {
@@ -3879,7 +3878,6 @@
pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
pw.println(" getUserUnlockedWithBiometric()="
+ getUserUnlockedWithBiometric(getCurrentUser()));
- pw.println(" mWakeOnFingerprintAcquiredStart=" + mWakeOnFingerprintAcquiredStart);
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
pw.println(" " + data.toString());
@@ -3895,7 +3893,7 @@
for (int subId : mServiceStates.keySet()) {
pw.println(" " + subId + "=" + mServiceStates.get(subId));
}
- if (mFpm != null && mFpm.isHardwareDetected()) {
+ if (mFpm != null && !mFingerprintSensorProperties.isEmpty()) {
final int userId = mUserTracker.getUserId();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
@@ -3944,7 +3942,7 @@
mFingerprintListenBuffer.toList()
).printTableData(pw);
}
- if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
+ if (mFaceManager != null && !mFaceSensorProperties.isEmpty()) {
final int userId = mUserTracker.getUserId();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 34a5ef7..abad0be 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -88,7 +88,9 @@
Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
Color.WHITE,
mDozeAmount);
- mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+ int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
+ com.android.internal.R.attr.colorSurface);
+ mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor));
mBgView.setAlpha(1f - mDozeAmount);
mBgView.setVisibility(View.VISIBLE);
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1bf63e59..413a3ca 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -431,6 +431,7 @@
pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
+ pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx);
if (mView != null) {
mView.dump(pw, args);
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index b159714..35cae09 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -47,7 +47,6 @@
import com.android.systemui.R;
import java.util.ArrayList;
-import java.util.Stack;
/**
* A View similar to a textView which contains password text and can animate when the text is
@@ -92,7 +91,6 @@
private final int mGravity;
private ArrayList<CharState> mTextChars = new ArrayList<>();
private String mText = "";
- private Stack<CharState> mCharPool = new Stack<>();
private int mDotSize;
private PowerManager mPM;
private int mCharPadding;
@@ -310,13 +308,7 @@
}
private CharState obtainCharState(char c) {
- CharState charState;
- if(mCharPool.isEmpty()) {
- charState = new CharState();
- } else {
- charState = mCharPool.pop();
- charState.reset();
- }
+ CharState charState = new CharState();
charState.whichChar = c;
return charState;
}
@@ -343,8 +335,6 @@
maxDelay = Math.min(maxDelay, RESET_MAX_DELAY) + DISAPPEAR_DURATION;
charState.startRemoveAnimation(startDelay, maxDelay);
charState.removeDotSwapCallbacks();
- } else {
- mCharPool.push(charState);
}
}
if (!animated) {
@@ -421,8 +411,6 @@
public void onAnimationEnd(Animator animation) {
if (!mCancelled) {
mTextChars.remove(CharState.this);
- mCharPool.push(CharState.this);
- reset();
cancelAnimator(textTranslateAnimator);
textTranslateAnimator = null;
}
@@ -518,21 +506,6 @@
}
};
- void reset() {
- whichChar = 0;
- currentTextSizeFactor = 0.0f;
- currentDotSizeFactor = 0.0f;
- currentWidthFactor = 0.0f;
- cancelAnimator(textAnimator);
- textAnimator = null;
- cancelAnimator(dotAnimator);
- dotAnimator = null;
- cancelAnimator(widthAnimator);
- widthAnimator = null;
- currentTextTranslationY = 1.0f;
- removeDotSwapCallbacks();
- }
-
void startRemoveAnimation(long startDelay, long widthDelay) {
boolean dotNeedsAnimation = (currentDotSizeFactor > 0.0f && dotAnimator == null)
|| (dotAnimator != null && dotAnimationIsGrowing);
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
index 0cbf8bc..5ad21df 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
@@ -20,13 +20,13 @@
import com.android.keyguard.KeyguardHostViewController;
import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import dagger.BindsInstance;
import dagger.Subcomponent;
/**
- * Dagger Subcomponent for the {@link KeyguardBouncer}.
+ * Dagger Subcomponent for the {@link PrimaryBouncerInteractor}.
*/
@Subcomponent(modules = {KeyguardBouncerModule.class})
@KeyguardBouncerScope
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index ef067b8..cb7a0a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -29,7 +29,7 @@
import com.android.systemui.R;
import com.android.systemui.biometrics.SideFpsController;
import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import java.util.Optional;
@@ -39,7 +39,7 @@
import dagger.Provides;
/**
- * Module to create and access view related to the {@link KeyguardBouncer}.
+ * Module to create and access view related to the {@link PrimaryBouncerInteractor}.
*/
@Module
public interface KeyguardBouncerModule {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 5b42455..201a1d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -431,4 +431,20 @@
str1 = PowerManager.wakeReasonToString(pmWakeReason)
}, { "Skip updating face listening state on wakeup from $str1"})
}
+
+ fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
+ logBuffer.log(TAG, VERBOSE, {
+ bool1 = assistantVisible
+ }, {
+ "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
+ })
+ }
+
+ fun logAssistantVisible(assistantVisible: Boolean) {
+ logBuffer.log(TAG, VERBOSE, {
+ bool1 = assistantVisible
+ }, {
+ "Updating mAssistantVisible to new value: $bool1"
+ })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e6f559b..71f98fa 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -36,10 +36,10 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
import android.hardware.graphics.common.AlphaInterpretation;
import android.hardware.graphics.common.DisplayDecorationSupport;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.SystemProperties;
import android.os.Trace;
import android.provider.Settings.Secure;
@@ -76,6 +76,7 @@
import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegate;
import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.tuner.TunerService;
@@ -120,7 +121,7 @@
R.id.display_cutout_bottom
};
- private DisplayManager mDisplayManager;
+ private DisplayTracker mDisplayTracker;
@VisibleForTesting
protected boolean mIsRegistered;
private final Context mContext;
@@ -128,7 +129,7 @@
private final TunerService mTunerService;
private final SecureSettings mSecureSettings;
@VisibleForTesting
- DisplayManager.DisplayListener mDisplayListener;
+ DisplayTracker.Callback mDisplayListener;
private CameraAvailabilityListener mCameraListener;
private final UserTracker mUserTracker;
private final PrivacyDotViewController mDotViewController;
@@ -302,6 +303,7 @@
SecureSettings secureSettings,
TunerService tunerService,
UserTracker userTracker,
+ DisplayTracker displayTracker,
PrivacyDotViewController dotViewController,
ThreadFactory threadFactory,
PrivacyDotDecorProviderFactory dotFactory,
@@ -311,6 +313,7 @@
mSecureSettings = secureSettings;
mTunerService = tunerService;
mUserTracker = userTracker;
+ mDisplayTracker = displayTracker;
mDotViewController = dotViewController;
mThreadFactory = threadFactory;
mDotFactory = dotFactory;
@@ -376,7 +379,6 @@
private void startOnScreenDecorationsThread() {
Trace.beginSection("ScreenDecorations#startOnScreenDecorationsThread");
mWindowManager = mContext.getSystemService(WindowManager.class);
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
mContext.getDisplay().getDisplayInfo(mDisplayInfo);
mRotation = mDisplayInfo.rotation;
mDisplayMode = mDisplayInfo.getMode();
@@ -393,17 +395,7 @@
setupDecorations();
setupCameraListener();
- mDisplayListener = new DisplayManager.DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- // do nothing
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- // do nothing
- }
-
+ mDisplayListener = new DisplayTracker.Callback() {
@Override
public void onDisplayChanged(int displayId) {
mContext.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -474,8 +466,7 @@
}
}
};
-
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ mDisplayTracker.addDisplayChangeCallback(mDisplayListener, new HandlerExecutor(mHandler));
updateConfiguration();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index c5d4bbe..00f5ac2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -36,7 +36,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
-import android.view.Display;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -50,6 +49,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
@@ -180,6 +180,7 @@
private final Context mContext;
private final UserTracker mUserTracker;
private final Optional<Recents> mRecentsOptional;
+ private final DisplayTracker mDisplayTracker;
private Locale mLocale;
private final AccessibilityManager mA11yManager;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -194,11 +195,13 @@
NotificationShadeWindowController notificationShadeController,
ShadeController shadeController,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
- Optional<Recents> recentsOptional) {
+ Optional<Recents> recentsOptional,
+ DisplayTracker displayTracker) {
mContext = context;
mUserTracker = userTracker;
mShadeController = shadeController;
mRecentsOptional = recentsOptional;
+ mDisplayTracker = displayTracker;
mReceiver = new SystemActionsBroadcastReceiver();
mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
mA11yManager = (AccessibilityManager) mContext.getSystemService(
@@ -207,7 +210,8 @@
// Saving in instance variable since to prevent GC since
// NotificationShadeWindowController.registerCallback() only keeps weak references.
mNotificationShadeCallback =
- (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing, panelExpanded) ->
+ (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing, panelExpanded,
+ isDreaming) ->
registerOrUnregisterDismissNotificationShadeAction();
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
}
@@ -522,7 +526,7 @@
private void handleAccessibilityButton() {
AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
- Display.DEFAULT_DISPLAY);
+ mDisplayTracker.getDefaultDisplayId());
}
private void handleAccessibilityButtonChooser() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 348c2ee..3653bc8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -40,6 +40,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
@@ -64,6 +65,7 @@
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
private final OverviewProxyService mOverviewProxyService;
+ private final DisplayTracker mDisplayTracker;
private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
private SysUiState mSysUiState;
@@ -115,7 +117,7 @@
public WindowMagnification(Context context, @Main Handler mainHandler,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings, DisplayTracker displayTracker) {
mContext = context;
mHandler = mainHandler;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
@@ -123,6 +125,7 @@
mModeSwitchesController = modeSwitchesController;
mSysUiState = sysUiState;
mOverviewProxyService = overviewProxyService;
+ mDisplayTracker = displayTracker;
mMagnificationControllerSupplier = new ControllerSupplier(context,
mHandler, this, context.getSystemService(DisplayManager.class), sysUiState,
secureSettings);
@@ -144,14 +147,14 @@
private void updateSysUiStateFlag() {
//TODO(b/187510533): support multi-display once SysuiState supports it.
final WindowMagnificationController controller =
- mMagnificationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY);
+ mMagnificationControllerSupplier.valueAt(mDisplayTracker.getDefaultDisplayId());
if (controller != null) {
controller.updateSysUIStateFlag();
} else {
// The instance is initialized when there is an IPC request. Considering
// self-crash cases, we need to reset the flag in such situation.
mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
- .commitUpdate(Display.DEFAULT_DISPLAY);
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 7fedf16..2c413a2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -17,7 +17,6 @@
package com.android.systemui.accessibility.floatingmenu;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static com.android.systemui.flags.Flags.A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS;
@@ -40,6 +39,7 @@
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -61,6 +61,7 @@
private final AccessibilityManager mAccessibilityManager;
private final FeatureFlags mFeatureFlags;
private final SecureSettings mSecureSettings;
+ private final DisplayTracker mDisplayTracker;
@VisibleForTesting
IAccessibilityFloatingMenu mFloatingMenu;
private int mBtnMode;
@@ -105,7 +106,8 @@
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
KeyguardUpdateMonitor keyguardUpdateMonitor,
FeatureFlags featureFlags,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings,
+ DisplayTracker displayTracker) {
mContext = context;
mWindowManager = windowManager;
mDisplayManager = displayManager;
@@ -115,6 +117,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mFeatureFlags = featureFlags;
mSecureSettings = secureSettings;
+ mDisplayTracker = displayTracker;
mIsKeyguardVisible = false;
}
@@ -185,7 +188,8 @@
private void showFloatingMenu() {
if (mFloatingMenu == null) {
if (mFeatureFlags.isEnabled(A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS)) {
- final Display defaultDisplay = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ final Display defaultDisplay = mDisplayManager.getDisplay(
+ mDisplayTracker.getDefaultDisplayId());
final Context windowContext = mContext.createWindowContext(defaultDisplay,
TYPE_NAVIGATION_BAR_PANEL, /* options= */ null);
mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index aad708a..0538e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -42,6 +42,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -66,6 +67,9 @@
private static final float DEFAULT_MENU_POSITION_X_PERCENT = 1.0f;
@FloatRange(from = 0.0, to = 1.0)
+ private static final float DEFAULT_MENU_POSITION_X_PERCENT_RTL = 0.0f;
+
+ @FloatRange(from = 0.0, to = 1.0)
private static final float DEFAULT_MENU_POSITION_Y_PERCENT = 0.77f;
private static final boolean DEFAULT_MOVE_TO_TUCKED_VALUE = false;
private static final boolean DEFAULT_HAS_SEEN_DOCK_TOOLTIP_VALUE = false;
@@ -226,8 +230,12 @@
final String absolutePositionString = Prefs.getString(mContext,
Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
+ final float defaultPositionXPercent =
+ mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? DEFAULT_MENU_POSITION_X_PERCENT_RTL
+ : DEFAULT_MENU_POSITION_X_PERCENT;
return TextUtils.isEmpty(absolutePositionString)
- ? new Position(DEFAULT_MENU_POSITION_X_PERCENT, DEFAULT_MENU_POSITION_Y_PERCENT)
+ ? new Position(defaultPositionXPercent, DEFAULT_MENU_POSITION_Y_PERCENT)
: Position.fromString(absolutePositionString);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 86f0d06..665a398 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -1,7 +1,5 @@
package com.android.systemui.assist;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
@@ -35,6 +33,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -122,6 +121,7 @@
protected final Lazy<SysUiState> mSysUiState;
protected final AssistLogger mAssistLogger;
private final UserTracker mUserTracker;
+ private final DisplayTracker mDisplayTracker;
private final SecureSettings mSecureSettings;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -141,6 +141,7 @@
AssistLogger assistLogger,
@Main Handler uiHandler,
UserTracker userTracker,
+ DisplayTracker displayTracker,
SecureSettings secureSettings) {
mContext = context;
mDeviceProvisionedController = controller;
@@ -150,6 +151,7 @@
mPhoneStateMonitor = phoneStateMonitor;
mAssistLogger = assistLogger;
mUserTracker = userTracker;
+ mDisplayTracker = displayTracker;
mSecureSettings = secureSettings;
registerVoiceInteractionSessionListener();
@@ -214,7 +216,7 @@
.setFlag(
SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
hints.getBoolean(CONSTRAINED_KEY, false))
- .commitUpdate(DEFAULT_DISPLAY);
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index e42f051..3e5d16a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -878,7 +878,7 @@
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
windowFlags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 4b57d45..04a2689 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -55,11 +55,11 @@
private val fadeDuration = 83L
private val retractDuration = 400L
private var alphaInDuration: Long = 0
- private var unlockedRippleInProgress: Boolean = false
private val dwellShader = DwellRippleShader()
private val dwellPaint = Paint()
private val rippleShader = RippleShader()
private val ripplePaint = Paint()
+ private var unlockedRippleAnimator: AnimatorSet? = null
private var fadeDwellAnimator: Animator? = null
private var retractDwellAnimator: Animator? = null
private var dwellPulseOutAnimator: Animator? = null
@@ -205,7 +205,7 @@
* Plays a ripple animation that grows to the dwellRadius with distortion.
*/
fun startDwellRipple(isDozing: Boolean) {
- if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
+ if (unlockedRippleAnimator?.isRunning == true || dwellPulseOutAnimator?.isRunning == true) {
return
}
@@ -262,9 +262,7 @@
* Ripple that bursts outwards from the position of the sensor to the edges of the screen
*/
fun startUnlockedRipple(onAnimationEnd: Runnable?) {
- if (unlockedRippleInProgress) {
- return // Ignore if ripple effect is already playing
- }
+ unlockedRippleAnimator?.cancel()
val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
@@ -289,14 +287,13 @@
}
}
- val animatorSet = AnimatorSet().apply {
+ unlockedRippleAnimator = AnimatorSet().apply {
playTogether(
rippleAnimator,
alphaInAnimator
)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
- unlockedRippleInProgress = true
rippleShader.rippleFill = false
drawRipple = true
visibility = VISIBLE
@@ -304,13 +301,13 @@
override fun onAnimationEnd(animation: Animator?) {
onAnimationEnd?.run()
- unlockedRippleInProgress = false
drawRipple = false
visibility = GONE
+ unlockedRippleAnimator = null
}
})
}
- animatorSet.start()
+ unlockedRippleAnimator?.start()
}
fun resetRippleAlpha() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 6f594d5..c799e91 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -54,12 +54,18 @@
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
private const val TAG = "SideFpsController"
@@ -79,6 +85,9 @@
displayManager: DisplayManager,
@Main private val mainExecutor: DelayableExecutor,
@Main private val handler: Handler,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ @Application private val scope: CoroutineScope,
+ private val featureFlags: FeatureFlags,
dumpManager: DumpManager
) : Dumpable {
val requests: HashSet<SideFpsUiRequestSource> = HashSet()
@@ -168,9 +177,26 @@
}
)
overviewProxyService.addCallback(overviewProxyListener)
+ listenForAlternateBouncerVisibility()
+
dumpManager.registerDumpable(this)
}
+ private fun listenForAlternateBouncerVisibility() {
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(true)
+ if (featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER)) {
+ scope.launch {
+ alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
+ if (isVisible) {
+ show(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
+ } else {
+ hide(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
+ }
+ }
+ }
+ }
+ }
+
/** Shows the side fps overlay if not already shown. */
fun show(request: SideFpsUiRequestSource) {
requests.add(request)
@@ -423,4 +449,5 @@
AUTO_SHOW,
/** Pin, pattern or password bouncer */
PRIMARY_BOUNCER,
+ ALTERNATE_BOUNCER
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index b4b3fae..6680787 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -262,7 +262,9 @@
}
REASON_AUTH_KEYGUARD -> {
UdfpsKeyguardViewController(
- view.addUdfpsView(R.layout.udfps_keyguard_view),
+ view.addUdfpsView(R.layout.udfps_keyguard_view) {
+ updateSensorLocation(sensorBounds)
+ },
statusBarStateController,
shadeExpansionStateManager,
statusBarKeyguardViewManager,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 4017665..c82e6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -68,10 +68,7 @@
mEnrollHelper = enrollHelper;
mView.setEnrollHelper(mEnrollHelper);
mView.setProgressBarRadius(mEnrollProgressBarRadius);
-
- if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- mView.mUseExpandedOverlay = true;
- }
+ mView.mUseExpandedOverlay = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 339b8ca..ee9081c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.MathUtils;
@@ -34,6 +35,7 @@
import android.widget.ImageView;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
@@ -65,6 +67,7 @@
private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
private int mAlpha; // 0-255
private float mScaleFactor = 1;
+ private Rect mSensorBounds = new Rect();
// AOD anti-burn-in offsets
private final int mMaxBurnInOffsetX;
@@ -76,8 +79,6 @@
private int mAnimationType = ANIMATION_NONE;
private boolean mFullyInflated;
- private LayoutParams mParams;
-
public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mFingerprintDrawable = new UdfpsFpDrawable(context);
@@ -88,10 +89,7 @@
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
+ public void startIconAsyncInflate() {
// inflate Lottie views on a background thread in case it takes a while to inflate
AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext);
inflater.inflate(R.layout.udfps_keyguard_view_internal, this,
@@ -242,20 +240,8 @@
updateAlpha();
}
- @Override
- void onSensorRectUpdated(RectF bounds) {
- super.onSensorRectUpdated(bounds);
-
- if (mUseExpandedOverlay) {
- mParams = new LayoutParams((int) bounds.width(), (int) bounds.height());
- RectF converted = getBoundsRelativeToView(bounds);
- mParams.setMargins(
- (int) converted.left,
- (int) converted.top,
- (int) converted.right,
- (int) converted.bottom
- );
- }
+ void updateSensorLocation(@NonNull Rect sensorBounds) {
+ mSensorBounds.set(sensorBounds);
}
/**
@@ -313,7 +299,17 @@
updateAlpha();
if (mUseExpandedOverlay) {
- parent.addView(view, mParams);
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.width = mSensorBounds.width();
+ lp.height = mSensorBounds.height();
+ RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds));
+ lp.setMargins(
+ (int) relativeToView.left,
+ (int) relativeToView.top,
+ (int) relativeToView.right,
+ (int) relativeToView.bottom
+ );
+ parent.addView(view, lp);
} else {
parent.addView(view);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 63a1b76..9bccafb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -32,7 +32,6 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -149,21 +148,6 @@
}
}
- private val mPrimaryBouncerExpansionCallback: PrimaryBouncerExpansionCallback =
- object : PrimaryBouncerExpansionCallback {
- override fun onExpansionChanged(expansion: Float) {
- inputBouncerHiddenAmount = expansion
- updateAlpha()
- updatePauseAuth()
- }
-
- override fun onVisibilityChanged(isVisible: Boolean) {
- updateBouncerHiddenAmount()
- updateAlpha()
- updatePauseAuth()
- }
- }
-
private val configurationListener: ConfigurationController.ConfigurationListener =
object : ConfigurationController.ConfigurationListener {
override fun onUiModeChanged() {
@@ -315,14 +299,6 @@
statusBarState = statusBarStateController.state
qsExpansion = keyguardViewManager.qsExpansion
keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
- if (!isModernBouncerEnabled) {
- val bouncer = keyguardViewManager.primaryBouncer
- bouncer?.expansion?.let {
- mPrimaryBouncerExpansionCallback.onExpansionChanged(it)
- bouncer.addBouncerExpansionCallback(mPrimaryBouncerExpansionCallback)
- }
- updateBouncerHiddenAmount()
- }
configurationController.addCallback(configurationListener)
shadeExpansionStateManager.addExpansionListener(shadeExpansionListener)
updateScaleFactor()
@@ -334,6 +310,7 @@
lockScreenShadeTransitionController.udfpsKeyguardViewController = this
activityLaunchAnimator.addListener(activityLaunchAnimatorListener)
view.mUseExpandedOverlay = useExpandedOverlay
+ view.startIconAsyncInflate()
}
override fun onViewDetached() {
@@ -352,11 +329,6 @@
}
activityLaunchAnimator.removeListener(activityLaunchAnimatorListener)
keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
- if (!isModernBouncerEnabled) {
- keyguardViewManager.primaryBouncer?.removeBouncerExpansionCallback(
- mPrimaryBouncerExpansionCallback
- )
- }
}
override fun dump(pw: PrintWriter, args: Array<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
index 3a01cd5..39ea936 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
@@ -54,9 +54,12 @@
return when (event.actionMasked) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_POINTER_DOWN,
- MotionEvent.ACTION_MOVE -> processActionMove(preprocess())
+ MotionEvent.ACTION_MOVE,
+ MotionEvent.ACTION_HOVER_ENTER,
+ MotionEvent.ACTION_HOVER_MOVE -> processActionMove(preprocess())
MotionEvent.ACTION_UP,
- MotionEvent.ACTION_POINTER_UP ->
+ MotionEvent.ACTION_POINTER_UP,
+ MotionEvent.ACTION_HOVER_EXIT ->
processActionUp(preprocess(), event.getPointerId(event.actionIndex))
MotionEvent.ACTION_CANCEL -> processActionCancel(NormalizedTouchData())
else ->
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 805a20a..1c26841 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -18,7 +18,6 @@
import static android.content.ClipDescription.CLASSIFICATION_COMPLETE;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN;
@@ -29,7 +28,6 @@
import android.content.ClipboardManager;
import android.content.Context;
import android.os.SystemProperties;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;
@@ -37,9 +35,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.util.DeviceConfigProxy;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -59,42 +54,28 @@
"com.android.systemui.SUPPRESS_CLIPBOARD_OVERLAY";
private final Context mContext;
- private final DeviceConfigProxy mDeviceConfig;
private final Provider<ClipboardOverlayController> mOverlayProvider;
- private final ClipboardOverlayControllerLegacyFactory mOverlayFactory;
private final ClipboardToast mClipboardToast;
private final ClipboardManager mClipboardManager;
private final UiEventLogger mUiEventLogger;
- private final FeatureFlags mFeatureFlags;
- private boolean mUsingNewOverlay;
private ClipboardOverlay mClipboardOverlay;
@Inject
- public ClipboardListener(Context context, DeviceConfigProxy deviceConfigProxy,
+ public ClipboardListener(Context context,
Provider<ClipboardOverlayController> clipboardOverlayControllerProvider,
- ClipboardOverlayControllerLegacyFactory overlayFactory,
ClipboardToast clipboardToast,
ClipboardManager clipboardManager,
- UiEventLogger uiEventLogger,
- FeatureFlags featureFlags) {
+ UiEventLogger uiEventLogger) {
mContext = context;
- mDeviceConfig = deviceConfigProxy;
mOverlayProvider = clipboardOverlayControllerProvider;
- mOverlayFactory = overlayFactory;
mClipboardToast = clipboardToast;
mClipboardManager = clipboardManager;
mUiEventLogger = uiEventLogger;
- mFeatureFlags = featureFlags;
-
- mUsingNewOverlay = mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR);
}
@Override
public void start() {
- if (mDeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
- mClipboardManager.addPrimaryClipChangedListener(this);
- }
+ mClipboardManager.addPrimaryClipChangedListener(this);
}
@Override
@@ -120,14 +101,8 @@
return;
}
- boolean enabled = mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR);
- if (mClipboardOverlay == null || enabled != mUsingNewOverlay) {
- mUsingNewOverlay = enabled;
- if (enabled) {
- mClipboardOverlay = mOverlayProvider.get();
- } else {
- mClipboardOverlay = mOverlayFactory.create(mContext);
- }
+ if (mClipboardOverlay == null) {
+ mClipboardOverlay = mOverlayProvider.get();
mUiEventLogger.log(CLIPBOARD_OVERLAY_ENTERED, 0, clipSource);
} else {
mUiEventLogger.log(CLIPBOARD_OVERLAY_UPDATED, 0, clipSource);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index f97d6af..8c8ee8a 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -17,7 +17,6 @@
package com.android.systemui.clipboardoverlay;
import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
@@ -72,6 +71,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.screenshot.TimeoutHandler;
+import com.android.systemui.settings.DisplayTracker;
import java.io.IOException;
import java.util.Optional;
@@ -96,6 +96,7 @@
private final ClipboardLogger mClipboardLogger;
private final BroadcastDispatcher mBroadcastDispatcher;
private final DisplayManager mDisplayManager;
+ private final DisplayTracker mDisplayTracker;
private final ClipboardOverlayWindow mWindow;
private final TimeoutHandler mTimeoutHandler;
private final ClipboardOverlayUtils mClipboardUtils;
@@ -186,9 +187,11 @@
FeatureFlags featureFlags,
ClipboardOverlayUtils clipboardUtils,
@Background Executor bgExecutor,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ DisplayTracker displayTracker) {
mBroadcastDispatcher = broadcastDispatcher;
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
+ mDisplayTracker = displayTracker;
final Context displayContext = context.createDisplayContext(getDefaultDisplay());
mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
@@ -514,7 +517,7 @@
}
private Display getDefaultDisplay() {
- return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ return mDisplayManager.getDisplay(mDisplayTracker.getDefaultDisplayId());
}
static class ClipboardLogger {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacy.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacy.java
deleted file mode 100644
index 3a040829..0000000
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacy.java
+++ /dev/null
@@ -1,963 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.clipboardoverlay;
-
-import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
-
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_EDIT_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHARE_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
-
-import static java.util.Objects.requireNonNull;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.annotation.MainThread;
-import android.app.ICompatCameraControlCallback;
-import android.app.RemoteAction;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Insets;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.Icon;
-import android.hardware.display.DisplayManager;
-import android.hardware.input.InputManager;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Looper;
-import android.provider.DeviceConfig;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Size;
-import android.util.TypedValue;
-import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.Gravity;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.InputMonitor;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewRootImpl;
-import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassificationManager;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.policy.PhoneWindow;
-import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.screenshot.DraggableConstraintLayout;
-import com.android.systemui.screenshot.FloatingWindowUtil;
-import com.android.systemui.screenshot.OverlayActionChip;
-import com.android.systemui.screenshot.TimeoutHandler;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-/**
- * Controls state and UI for the overlay that appears when something is added to the clipboard
- */
-public class ClipboardOverlayControllerLegacy implements ClipboardListener.ClipboardOverlay {
- private static final String TAG = "ClipboardOverlayCtrlr";
- private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
-
- /** Constants for screenshot/copy deconflicting */
- public static final String SCREENSHOT_ACTION = "com.android.systemui.SCREENSHOT";
- public static final String SELF_PERMISSION = "com.android.systemui.permission.SELF";
- public static final String COPY_OVERLAY_ACTION = "com.android.systemui.COPY";
-
- private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
-
- private static final int CLIPBOARD_DEFAULT_TIMEOUT_MILLIS = 6000;
- private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
- private static final int FONT_SEARCH_STEP_PX = 4;
-
- private final Context mContext;
- private final ClipboardLogger mClipboardLogger;
- private final BroadcastDispatcher mBroadcastDispatcher;
- private final DisplayManager mDisplayManager;
- private final DisplayMetrics mDisplayMetrics;
- private final WindowManager mWindowManager;
- private final WindowManager.LayoutParams mWindowLayoutParams;
- private final PhoneWindow mWindow;
- private final TimeoutHandler mTimeoutHandler;
- private final AccessibilityManager mAccessibilityManager;
- private final TextClassifier mTextClassifier;
-
- private final DraggableConstraintLayout mView;
- private final View mClipboardPreview;
- private final ImageView mImagePreview;
- private final TextView mTextPreview;
- private final TextView mHiddenPreview;
- private final View mPreviewBorder;
- private final OverlayActionChip mEditChip;
- private final OverlayActionChip mShareChip;
- private final OverlayActionChip mRemoteCopyChip;
- private final View mActionContainerBackground;
- private final View mDismissButton;
- private final LinearLayout mActionContainer;
- private final ArrayList<OverlayActionChip> mActionChips = new ArrayList<>();
-
- private Runnable mOnSessionCompleteListener;
-
- private InputMonitor mInputMonitor;
- private InputEventReceiver mInputEventReceiver;
-
- private BroadcastReceiver mCloseDialogsReceiver;
- private BroadcastReceiver mScreenshotReceiver;
-
- private boolean mBlockAttach = false;
- private Animator mExitAnimator;
- private Animator mEnterAnimator;
- private final int mOrientation;
- private boolean mKeyboardVisible;
-
-
- public ClipboardOverlayControllerLegacy(Context context,
- BroadcastDispatcher broadcastDispatcher,
- BroadcastSender broadcastSender,
- TimeoutHandler timeoutHandler, UiEventLogger uiEventLogger) {
- mBroadcastDispatcher = broadcastDispatcher;
- mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
- final Context displayContext = context.createDisplayContext(getDefaultDisplay());
- mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
-
- mClipboardLogger = new ClipboardLogger(uiEventLogger);
-
- mAccessibilityManager = AccessibilityManager.getInstance(mContext);
- mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
- .getTextClassifier();
-
- mWindowManager = mContext.getSystemService(WindowManager.class);
-
- mDisplayMetrics = new DisplayMetrics();
- mContext.getDisplay().getRealMetrics(mDisplayMetrics);
-
- mTimeoutHandler = timeoutHandler;
- mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
-
- // Setup the window that we are going to use
- mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
- mWindowLayoutParams.setTitle("ClipboardOverlay");
-
- mWindow = FloatingWindowUtil.getFloatingWindow(mContext);
- mWindow.setWindowManager(mWindowManager, null, null);
-
- setWindowFocusable(false);
-
- mView = (DraggableConstraintLayout)
- LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay_legacy, null);
- mActionContainerBackground =
- requireNonNull(mView.findViewById(R.id.actions_container_background));
- mActionContainer = requireNonNull(mView.findViewById(R.id.actions));
- mClipboardPreview = requireNonNull(mView.findViewById(R.id.clipboard_preview));
- mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview));
- mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
- mHiddenPreview = requireNonNull(mView.findViewById(R.id.hidden_preview));
- mPreviewBorder = requireNonNull(mView.findViewById(R.id.preview_border));
- mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
- mShareChip = requireNonNull(mView.findViewById(R.id.share_chip));
- mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
- mEditChip.setAlpha(1);
- mShareChip.setAlpha(1);
- mRemoteCopyChip.setAlpha(1);
- mDismissButton = requireNonNull(mView.findViewById(R.id.dismiss_button));
-
- mShareChip.setContentDescription(mContext.getString(com.android.internal.R.string.share));
- mView.setCallbacks(new DraggableConstraintLayout.SwipeDismissCallbacks() {
- @Override
- public void onInteraction() {
- mTimeoutHandler.resetTimeout();
- }
-
- @Override
- public void onSwipeDismissInitiated(Animator animator) {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
- mExitAnimator = animator;
- }
-
- @Override
- public void onDismissComplete() {
- hideImmediate();
- }
- });
-
- mTextPreview.getViewTreeObserver().addOnPreDrawListener(() -> {
- int availableHeight = mTextPreview.getHeight()
- - (mTextPreview.getPaddingTop() + mTextPreview.getPaddingBottom());
- mTextPreview.setMaxLines(availableHeight / mTextPreview.getLineHeight());
- return true;
- });
-
- mDismissButton.setOnClickListener(view -> {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
- animateOut();
- });
-
- mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
- mRemoteCopyChip.setIcon(
- Icon.createWithResource(mContext, R.drawable.ic_baseline_devices_24), true);
- mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
- mOrientation = mContext.getResources().getConfiguration().orientation;
-
- attachWindow();
- withWindowAttached(() -> {
- mWindow.setContentView(mView);
- WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
- mKeyboardVisible = insets.isVisible(WindowInsets.Type.ime());
- updateInsets(insets);
- mWindow.peekDecorView().getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- WindowInsets insets =
- mWindowManager.getCurrentWindowMetrics().getWindowInsets();
- boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime());
- if (keyboardVisible != mKeyboardVisible) {
- mKeyboardVisible = keyboardVisible;
- updateInsets(insets);
- }
- }
- });
- mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
- new ViewRootImpl.ActivityConfigCallback() {
- @Override
- public void onConfigurationChanged(Configuration overrideConfig,
- int newDisplayId) {
- if (mContext.getResources().getConfiguration().orientation
- != mOrientation) {
- mClipboardLogger.logSessionComplete(
- CLIPBOARD_OVERLAY_DISMISSED_OTHER);
- hideImmediate();
- }
- }
-
- @Override
- public void requestCompatCameraControl(
- boolean showControl, boolean transformationApplied,
- ICompatCameraControlCallback callback) {
- Log.w(TAG, "unexpected requestCompatCameraControl call");
- }
- });
- });
-
- mTimeoutHandler.setOnTimeoutRunnable(() -> {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
- animateOut();
- });
-
- mCloseDialogsReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
- animateOut();
- }
- }
- };
-
- mBroadcastDispatcher.registerReceiver(mCloseDialogsReceiver,
- new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS));
- mScreenshotReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (SCREENSHOT_ACTION.equals(intent.getAction())) {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
- animateOut();
- }
- }
- };
-
- mBroadcastDispatcher.registerReceiver(mScreenshotReceiver,
- new IntentFilter(SCREENSHOT_ACTION), null, null, Context.RECEIVER_EXPORTED,
- SELF_PERMISSION);
- monitorOutsideTouches();
-
- Intent copyIntent = new Intent(COPY_OVERLAY_ACTION);
- // Set package name so the system knows it's safe
- copyIntent.setPackage(mContext.getPackageName());
- broadcastSender.sendBroadcast(copyIntent, SELF_PERMISSION);
- }
-
- @Override // ClipboardListener.ClipboardOverlay
- public void setClipData(ClipData clipData, String clipSource) {
- if (mExitAnimator != null && mExitAnimator.isRunning()) {
- mExitAnimator.cancel();
- }
- reset();
- String accessibilityAnnouncement;
-
- boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
- && clipData.getDescription().getExtras()
- .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
- if (clipData == null || clipData.getItemCount() == 0) {
- showTextPreview(
- mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
- mTextPreview);
- accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
- } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
- ClipData.Item item = clipData.getItemAt(0);
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
- if (item.getTextLinks() != null) {
- AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
- }
- }
- if (isSensitive) {
- showEditableText(
- mContext.getResources().getString(R.string.clipboard_asterisks), true);
- } else {
- showEditableText(item.getText(), false);
- }
- showShareChip(clipData);
- accessibilityAnnouncement = mContext.getString(R.string.clipboard_text_copied);
- } else if (clipData.getItemAt(0).getUri() != null) {
- if (tryShowEditableImage(clipData.getItemAt(0).getUri(), isSensitive)) {
- showShareChip(clipData);
- accessibilityAnnouncement = mContext.getString(R.string.clipboard_image_copied);
- } else {
- accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
- }
- } else {
- showTextPreview(
- mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
- mTextPreview);
- accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
- }
- Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
- // Only show remote copy if it's available.
- PackageManager packageManager = mContext.getPackageManager();
- if (packageManager.resolveActivity(
- remoteCopyIntent, PackageManager.ResolveInfoFlags.of(0)) != null) {
- mRemoteCopyChip.setContentDescription(
- mContext.getString(R.string.clipboard_send_nearby_description));
- mRemoteCopyChip.setVisibility(View.VISIBLE);
- mRemoteCopyChip.setOnClickListener((v) -> {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
- mContext.startActivity(remoteCopyIntent);
- animateOut();
- });
- mActionContainerBackground.setVisibility(View.VISIBLE);
- } else {
- mRemoteCopyChip.setVisibility(View.GONE);
- }
- withWindowAttached(() -> {
- if (mEnterAnimator == null || !mEnterAnimator.isRunning()) {
- mView.post(this::animateIn);
- }
- mView.announceForAccessibility(accessibilityAnnouncement);
- });
- mTimeoutHandler.resetTimeout();
- }
-
- @Override // ClipboardListener.ClipboardOverlay
- public void setOnSessionCompleteListener(Runnable runnable) {
- mOnSessionCompleteListener = runnable;
- }
-
- private void classifyText(ClipData.Item item, String source) {
- ArrayList<RemoteAction> actions = new ArrayList<>();
- for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
- TextClassification classification = mTextClassifier.classifyText(
- item.getText(), link.getStart(), link.getEnd(), null);
- actions.addAll(classification.getActions());
- }
- mView.post(() -> {
- resetActionChips();
- if (actions.size() > 0) {
- mActionContainerBackground.setVisibility(View.VISIBLE);
- for (RemoteAction action : actions) {
- Intent targetIntent = action.getActionIntent().getIntent();
- ComponentName component = targetIntent.getComponent();
- if (component != null && !TextUtils.equals(source,
- component.getPackageName())) {
- OverlayActionChip chip = constructActionChip(action);
- mActionContainer.addView(chip);
- mActionChips.add(chip);
- break; // only show at most one action chip
- }
- }
- }
- });
- }
-
- private void showShareChip(ClipData clip) {
- mShareChip.setVisibility(View.VISIBLE);
- mActionContainerBackground.setVisibility(View.VISIBLE);
- mShareChip.setOnClickListener((v) -> shareContent(clip));
- }
-
- private OverlayActionChip constructActionChip(RemoteAction action) {
- OverlayActionChip chip = (OverlayActionChip) LayoutInflater.from(mContext).inflate(
- R.layout.overlay_action_chip, mActionContainer, false);
- chip.setText(action.getTitle());
- chip.setContentDescription(action.getTitle());
- chip.setIcon(action.getIcon(), false);
- chip.setPendingIntent(action.getActionIntent(), () -> {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
- animateOut();
- });
- chip.setAlpha(1);
- return chip;
- }
-
- private void monitorOutsideTouches() {
- InputManager inputManager = mContext.getSystemService(InputManager.class);
- mInputMonitor = inputManager.monitorGestureInput("clipboard overlay", 0);
- mInputEventReceiver = new InputEventReceiver(mInputMonitor.getInputChannel(),
- Looper.getMainLooper()) {
- @Override
- public void onInputEvent(InputEvent event) {
- if (event instanceof MotionEvent) {
- MotionEvent motionEvent = (MotionEvent) event;
- if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
- Region touchRegion = new Region();
-
- final Rect tmpRect = new Rect();
- mPreviewBorder.getBoundsOnScreen(tmpRect);
- tmpRect.inset(
- (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
- (int) FloatingWindowUtil.dpToPx(mDisplayMetrics,
- -SWIPE_PADDING_DP));
- touchRegion.op(tmpRect, Region.Op.UNION);
- mActionContainerBackground.getBoundsOnScreen(tmpRect);
- tmpRect.inset(
- (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
- (int) FloatingWindowUtil.dpToPx(mDisplayMetrics,
- -SWIPE_PADDING_DP));
- touchRegion.op(tmpRect, Region.Op.UNION);
- mDismissButton.getBoundsOnScreen(tmpRect);
- touchRegion.op(tmpRect, Region.Op.UNION);
- if (!touchRegion.contains(
- (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
- animateOut();
- }
- }
- }
- finishInputEvent(event, true /* handled */);
- }
- };
- }
-
- private void editImage(Uri uri) {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
- animateOut();
- }
-
- private void editText() {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
- animateOut();
- }
-
- private void shareContent(ClipData clip) {
- mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
- mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
- animateOut();
- }
-
- private void showSinglePreview(View v) {
- mTextPreview.setVisibility(View.GONE);
- mImagePreview.setVisibility(View.GONE);
- mHiddenPreview.setVisibility(View.GONE);
- v.setVisibility(View.VISIBLE);
- }
-
- private void showTextPreview(CharSequence text, TextView textView) {
- showSinglePreview(textView);
- final CharSequence truncatedText = text.subSequence(0, Math.min(500, text.length()));
- textView.setText(truncatedText);
- updateTextSize(truncatedText, textView);
-
- textView.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- if (right - left != oldRight - oldLeft) {
- updateTextSize(truncatedText, textView);
- }
- });
- mEditChip.setVisibility(View.GONE);
- }
-
- private void updateTextSize(CharSequence text, TextView textView) {
- Paint paint = new Paint(textView.getPaint());
- Resources res = textView.getResources();
- float minFontSize = res.getDimensionPixelSize(R.dimen.clipboard_overlay_min_font);
- float maxFontSize = res.getDimensionPixelSize(R.dimen.clipboard_overlay_max_font);
- if (isOneWord(text) && fitsInView(text, textView, paint, minFontSize)) {
- // If the text is a single word and would fit within the TextView at the min font size,
- // find the biggest font size that will fit.
- float fontSizePx = minFontSize;
- while (fontSizePx + FONT_SEARCH_STEP_PX < maxFontSize
- && fitsInView(text, textView, paint, fontSizePx + FONT_SEARCH_STEP_PX)) {
- fontSizePx += FONT_SEARCH_STEP_PX;
- }
- // Need to turn off autosizing, otherwise setTextSize is a no-op.
- textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_NONE);
- // It's possible to hit the max font size and not fill the width, so centering
- // horizontally looks better in this case.
- textView.setGravity(Gravity.CENTER);
- textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, (int) fontSizePx);
- } else {
- // Otherwise just stick with autosize.
- textView.setAutoSizeTextTypeUniformWithConfiguration((int) minFontSize,
- (int) maxFontSize, FONT_SEARCH_STEP_PX, TypedValue.COMPLEX_UNIT_PX);
- textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
- }
- }
-
- private static boolean fitsInView(CharSequence text, TextView textView, Paint paint,
- float fontSizePx) {
- paint.setTextSize(fontSizePx);
- float size = paint.measureText(text.toString());
- float availableWidth = textView.getWidth() - textView.getPaddingLeft()
- - textView.getPaddingRight();
- return size < availableWidth;
- }
-
- private static boolean isOneWord(CharSequence text) {
- return text.toString().split("\\s+", 2).length == 1;
- }
-
- private void showEditableText(CharSequence text, boolean hidden) {
- TextView textView = hidden ? mHiddenPreview : mTextPreview;
- showTextPreview(text, textView);
- View.OnClickListener listener = v -> editText();
- setAccessibilityActionToEdit(textView);
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON, false)) {
- mEditChip.setVisibility(View.VISIBLE);
- mActionContainerBackground.setVisibility(View.VISIBLE);
- mEditChip.setContentDescription(
- mContext.getString(R.string.clipboard_edit_text_description));
- mEditChip.setOnClickListener(listener);
- }
- textView.setOnClickListener(listener);
- }
-
- private boolean tryShowEditableImage(Uri uri, boolean isSensitive) {
- View.OnClickListener listener = v -> editImage(uri);
- ContentResolver resolver = mContext.getContentResolver();
- String mimeType = resolver.getType(uri);
- boolean isEditableImage = mimeType != null && mimeType.startsWith("image");
- if (isSensitive) {
- mHiddenPreview.setText(mContext.getString(R.string.clipboard_text_hidden));
- showSinglePreview(mHiddenPreview);
- if (isEditableImage) {
- mHiddenPreview.setOnClickListener(listener);
- setAccessibilityActionToEdit(mHiddenPreview);
- }
- } else if (isEditableImage) { // if the MIMEtype is image, try to load
- try {
- int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
- // The width of the view is capped, height maintains aspect ratio, so allow it to be
- // taller if needed.
- Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
- showSinglePreview(mImagePreview);
- mImagePreview.setImageBitmap(thumbnail);
- mImagePreview.setOnClickListener(listener);
- setAccessibilityActionToEdit(mImagePreview);
- } catch (IOException e) {
- Log.e(TAG, "Thumbnail loading failed", e);
- showTextPreview(
- mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
- mTextPreview);
- isEditableImage = false;
- }
- } else {
- showTextPreview(
- mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
- mTextPreview);
- }
- if (isEditableImage && DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON, false)) {
- mEditChip.setVisibility(View.VISIBLE);
- mActionContainerBackground.setVisibility(View.VISIBLE);
- mEditChip.setOnClickListener(listener);
- mEditChip.setContentDescription(
- mContext.getString(R.string.clipboard_edit_image_description));
- }
- return isEditableImage;
- }
-
- private void setAccessibilityActionToEdit(View view) {
- ViewCompat.replaceAccessibilityAction(view,
- AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK,
- mContext.getString(R.string.clipboard_edit), null);
- }
-
- private void animateIn() {
- if (mAccessibilityManager.isEnabled()) {
- mDismissButton.setVisibility(View.VISIBLE);
- }
- mEnterAnimator = getEnterAnimation();
- mEnterAnimator.start();
- }
-
- private void animateOut() {
- if (mExitAnimator != null && mExitAnimator.isRunning()) {
- return;
- }
- Animator anim = getExitAnimation();
- anim.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- if (!mCancelled) {
- hideImmediate();
- }
- }
- });
- mExitAnimator = anim;
- anim.start();
- }
-
- private Animator getEnterAnimation() {
- TimeInterpolator linearInterpolator = new LinearInterpolator();
- TimeInterpolator scaleInterpolator = new PathInterpolator(0, 0, 0, 1f);
- AnimatorSet enterAnim = new AnimatorSet();
-
- ValueAnimator rootAnim = ValueAnimator.ofFloat(0, 1);
- rootAnim.setInterpolator(linearInterpolator);
- rootAnim.setDuration(66);
- rootAnim.addUpdateListener(animation -> {
- mView.setAlpha(animation.getAnimatedFraction());
- });
-
- ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1);
- scaleAnim.setInterpolator(scaleInterpolator);
- scaleAnim.setDuration(333);
- scaleAnim.addUpdateListener(animation -> {
- float previewScale = MathUtils.lerp(.9f, 1f, animation.getAnimatedFraction());
- mClipboardPreview.setScaleX(previewScale);
- mClipboardPreview.setScaleY(previewScale);
- mPreviewBorder.setScaleX(previewScale);
- mPreviewBorder.setScaleY(previewScale);
-
- float pivotX = mClipboardPreview.getWidth() / 2f + mClipboardPreview.getX();
- mActionContainerBackground.setPivotX(pivotX - mActionContainerBackground.getX());
- mActionContainer.setPivotX(pivotX - ((View) mActionContainer.getParent()).getX());
- float actionsScaleX = MathUtils.lerp(.7f, 1f, animation.getAnimatedFraction());
- float actionsScaleY = MathUtils.lerp(.9f, 1f, animation.getAnimatedFraction());
- mActionContainer.setScaleX(actionsScaleX);
- mActionContainer.setScaleY(actionsScaleY);
- mActionContainerBackground.setScaleX(actionsScaleX);
- mActionContainerBackground.setScaleY(actionsScaleY);
- });
-
- ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
- alphaAnim.setInterpolator(linearInterpolator);
- alphaAnim.setDuration(283);
- alphaAnim.addUpdateListener(animation -> {
- float alpha = animation.getAnimatedFraction();
- mClipboardPreview.setAlpha(alpha);
- mPreviewBorder.setAlpha(alpha);
- mDismissButton.setAlpha(alpha);
- mActionContainer.setAlpha(alpha);
- });
-
- mActionContainer.setAlpha(0);
- mPreviewBorder.setAlpha(0);
- mClipboardPreview.setAlpha(0);
- enterAnim.play(rootAnim).with(scaleAnim);
- enterAnim.play(alphaAnim).after(50).after(rootAnim);
-
- enterAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mView.setAlpha(1);
- mTimeoutHandler.resetTimeout();
- }
- });
- return enterAnim;
- }
-
- private Animator getExitAnimation() {
- TimeInterpolator linearInterpolator = new LinearInterpolator();
- TimeInterpolator scaleInterpolator = new PathInterpolator(.3f, 0, 1f, 1f);
- AnimatorSet exitAnim = new AnimatorSet();
-
- ValueAnimator rootAnim = ValueAnimator.ofFloat(0, 1);
- rootAnim.setInterpolator(linearInterpolator);
- rootAnim.setDuration(100);
- rootAnim.addUpdateListener(anim -> mView.setAlpha(1 - anim.getAnimatedFraction()));
-
- ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1);
- scaleAnim.setInterpolator(scaleInterpolator);
- scaleAnim.setDuration(250);
- scaleAnim.addUpdateListener(animation -> {
- float previewScale = MathUtils.lerp(1f, .9f, animation.getAnimatedFraction());
- mClipboardPreview.setScaleX(previewScale);
- mClipboardPreview.setScaleY(previewScale);
- mPreviewBorder.setScaleX(previewScale);
- mPreviewBorder.setScaleY(previewScale);
-
- float pivotX = mClipboardPreview.getWidth() / 2f + mClipboardPreview.getX();
- mActionContainerBackground.setPivotX(pivotX - mActionContainerBackground.getX());
- mActionContainer.setPivotX(pivotX - ((View) mActionContainer.getParent()).getX());
- float actionScaleX = MathUtils.lerp(1f, .8f, animation.getAnimatedFraction());
- float actionScaleY = MathUtils.lerp(1f, .9f, animation.getAnimatedFraction());
- mActionContainer.setScaleX(actionScaleX);
- mActionContainer.setScaleY(actionScaleY);
- mActionContainerBackground.setScaleX(actionScaleX);
- mActionContainerBackground.setScaleY(actionScaleY);
- });
-
- ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
- alphaAnim.setInterpolator(linearInterpolator);
- alphaAnim.setDuration(166);
- alphaAnim.addUpdateListener(animation -> {
- float alpha = 1 - animation.getAnimatedFraction();
- mClipboardPreview.setAlpha(alpha);
- mPreviewBorder.setAlpha(alpha);
- mDismissButton.setAlpha(alpha);
- mActionContainer.setAlpha(alpha);
- });
-
- exitAnim.play(alphaAnim).with(scaleAnim);
- exitAnim.play(rootAnim).after(150).after(alphaAnim);
- return exitAnim;
- }
-
- private void hideImmediate() {
- // Note this may be called multiple times if multiple dismissal events happen at the same
- // time.
- mTimeoutHandler.cancelTimeout();
- final View decorView = mWindow.peekDecorView();
- if (decorView != null && decorView.isAttachedToWindow()) {
- mWindowManager.removeViewImmediate(decorView);
- }
- if (mCloseDialogsReceiver != null) {
- mBroadcastDispatcher.unregisterReceiver(mCloseDialogsReceiver);
- mCloseDialogsReceiver = null;
- }
- if (mScreenshotReceiver != null) {
- mBroadcastDispatcher.unregisterReceiver(mScreenshotReceiver);
- mScreenshotReceiver = null;
- }
- if (mInputEventReceiver != null) {
- mInputEventReceiver.dispose();
- mInputEventReceiver = null;
- }
- if (mInputMonitor != null) {
- mInputMonitor.dispose();
- mInputMonitor = null;
- }
- if (mOnSessionCompleteListener != null) {
- mOnSessionCompleteListener.run();
- }
- }
-
- private void resetActionChips() {
- for (OverlayActionChip chip : mActionChips) {
- mActionContainer.removeView(chip);
- }
- mActionChips.clear();
- }
-
- private void reset() {
- mView.setTranslationX(0);
- mView.setAlpha(0);
- mActionContainerBackground.setVisibility(View.GONE);
- mShareChip.setVisibility(View.GONE);
- mEditChip.setVisibility(View.GONE);
- mRemoteCopyChip.setVisibility(View.GONE);
- resetActionChips();
- mTimeoutHandler.cancelTimeout();
- mClipboardLogger.reset();
- }
-
- @MainThread
- private void attachWindow() {
- View decorView = mWindow.getDecorView();
- if (decorView.isAttachedToWindow() || mBlockAttach) {
- return;
- }
- mBlockAttach = true;
- mWindowManager.addView(decorView, mWindowLayoutParams);
- decorView.requestApplyInsets();
- mView.requestApplyInsets();
- decorView.getViewTreeObserver().addOnWindowAttachListener(
- new ViewTreeObserver.OnWindowAttachListener() {
- @Override
- public void onWindowAttached() {
- mBlockAttach = false;
- }
-
- @Override
- public void onWindowDetached() {
- }
- }
- );
- }
-
- private void withWindowAttached(Runnable action) {
- View decorView = mWindow.getDecorView();
- if (decorView.isAttachedToWindow()) {
- action.run();
- } else {
- decorView.getViewTreeObserver().addOnWindowAttachListener(
- new ViewTreeObserver.OnWindowAttachListener() {
- @Override
- public void onWindowAttached() {
- mBlockAttach = false;
- decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
- action.run();
- }
-
- @Override
- public void onWindowDetached() {
- }
- });
- }
- }
-
- private void updateInsets(WindowInsets insets) {
- int orientation = mContext.getResources().getConfiguration().orientation;
- FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) mView.getLayoutParams();
- if (p == null) {
- return;
- }
- DisplayCutout cutout = insets.getDisplayCutout();
- Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
- Insets imeInsets = insets.getInsets(WindowInsets.Type.ime());
- if (cutout == null) {
- p.setMargins(0, 0, 0, Math.max(imeInsets.bottom, navBarInsets.bottom));
- } else {
- Insets waterfall = cutout.getWaterfallInsets();
- if (orientation == ORIENTATION_PORTRAIT) {
- p.setMargins(
- waterfall.left,
- Math.max(cutout.getSafeInsetTop(), waterfall.top),
- waterfall.right,
- Math.max(imeInsets.bottom,
- Math.max(cutout.getSafeInsetBottom(),
- Math.max(navBarInsets.bottom, waterfall.bottom))));
- } else {
- p.setMargins(
- waterfall.left,
- waterfall.top,
- waterfall.right,
- Math.max(imeInsets.bottom,
- Math.max(navBarInsets.bottom, waterfall.bottom)));
- }
- }
- mView.setLayoutParams(p);
- mView.requestLayout();
- }
-
- private Display getDefaultDisplay() {
- return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
- }
-
- /**
- * Updates the window focusability. If the window is already showing, then it updates the
- * window immediately, otherwise the layout params will be applied when the window is next
- * shown.
- */
- private void setWindowFocusable(boolean focusable) {
- int flags = mWindowLayoutParams.flags;
- if (focusable) {
- mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- } else {
- mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- }
- if (mWindowLayoutParams.flags == flags) {
- return;
- }
- final View decorView = mWindow.peekDecorView();
- if (decorView != null && decorView.isAttachedToWindow()) {
- mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
- }
- }
-
- static class ClipboardLogger {
- private final UiEventLogger mUiEventLogger;
- private boolean mGuarded = false;
-
- ClipboardLogger(UiEventLogger uiEventLogger) {
- mUiEventLogger = uiEventLogger;
- }
-
- void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
- if (!mGuarded) {
- mGuarded = true;
- mUiEventLogger.log(event);
- }
- }
-
- void reset() {
- mGuarded = false;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacyFactory.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacyFactory.java
deleted file mode 100644
index 0d989a7..0000000
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacyFactory.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.clipboardoverlay;
-
-import android.content.Context;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.TimeoutHandler;
-
-import javax.inject.Inject;
-
-/**
- * A factory that churns out ClipboardOverlayControllerLegacys on demand.
- */
-@SysUISingleton
-public class ClipboardOverlayControllerLegacyFactory {
-
- private final UiEventLogger mUiEventLogger;
- private final BroadcastDispatcher mBroadcastDispatcher;
- private final BroadcastSender mBroadcastSender;
-
- @Inject
- public ClipboardOverlayControllerLegacyFactory(BroadcastDispatcher broadcastDispatcher,
- BroadcastSender broadcastSender, UiEventLogger uiEventLogger) {
- this.mBroadcastDispatcher = broadcastDispatcher;
- this.mBroadcastSender = broadcastSender;
- this.mUiEventLogger = uiEventLogger;
- }
-
- /**
- * One new ClipboardOverlayControllerLegacy, coming right up!
- */
- public ClipboardOverlayControllerLegacy create(Context context) {
- return new ClipboardOverlayControllerLegacy(context, mBroadcastDispatcher, mBroadcastSender,
- new TimeoutHandler(context), mUiEventLogger);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index 2244813..09b2e44 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.clipboardoverlay.dagger;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -28,6 +27,7 @@
import com.android.systemui.R;
import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
+import com.android.systemui.settings.DisplayTracker;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -46,8 +46,9 @@
*/
@Provides
@OverlayWindowContext
- static Context provideWindowContext(DisplayManager displayManager, Context context) {
- Display display = displayManager.getDisplay(DEFAULT_DISPLAY);
+ static Context provideWindowContext(DisplayManager displayManager,
+ DisplayTracker displayTracker, Context context) {
+ Display display = displayManager.getDisplay(displayTracker.getDefaultDisplayId());
return context.createWindowContext(display, TYPE_SCREENSHOT, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
new file mode 100644
index 0000000..2dd98dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.common.ui.view
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import kotlin.math.pow
+import kotlin.math.sqrt
+import kotlinx.coroutines.DisposableHandle
+
+/**
+ * View designed to handle long-presses.
+ *
+ * The view will not handle any long pressed by default. To set it up, set up a listener and, when
+ * ready to start consuming long-presses, set [setLongPressHandlingEnabled] to `true`.
+ */
+class LongPressHandlingView(
+ context: Context,
+ attrs: AttributeSet?,
+) :
+ View(
+ context,
+ attrs,
+ ) {
+ interface Listener {
+ /** Notifies that a long-press has been detected by the given view. */
+ fun onLongPressDetected(
+ view: View,
+ x: Int,
+ y: Int,
+ )
+
+ /** Notifies that the gesture was too short for a long press, it is actually a click. */
+ fun onSingleTapDetected(view: View) = Unit
+ }
+
+ var listener: Listener? = null
+
+ private val interactionHandler: LongPressHandlingViewInteractionHandler by lazy {
+ LongPressHandlingViewInteractionHandler(
+ postDelayed = { block, timeoutMs ->
+ val dispatchToken = Any()
+
+ handler.postDelayed(
+ block,
+ dispatchToken,
+ timeoutMs,
+ )
+
+ DisposableHandle { handler.removeCallbacksAndMessages(dispatchToken) }
+ },
+ isAttachedToWindow = ::isAttachedToWindow,
+ onLongPressDetected = { x, y ->
+ listener?.onLongPressDetected(
+ view = this,
+ x = x,
+ y = y,
+ )
+ },
+ onSingleTapDetected = { listener?.onSingleTapDetected(this@LongPressHandlingView) },
+ )
+ }
+
+ fun setLongPressHandlingEnabled(isEnabled: Boolean) {
+ interactionHandler.isLongPressHandlingEnabled = isEnabled
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ override fun onTouchEvent(event: MotionEvent?): Boolean {
+ return interactionHandler.onTouchEvent(event?.toModel())
+ }
+}
+
+private fun MotionEvent.toModel(): LongPressHandlingViewInteractionHandler.MotionEventModel {
+ return when (actionMasked) {
+ MotionEvent.ACTION_DOWN ->
+ LongPressHandlingViewInteractionHandler.MotionEventModel.Down(
+ x = x.toInt(),
+ y = y.toInt(),
+ )
+ MotionEvent.ACTION_MOVE ->
+ LongPressHandlingViewInteractionHandler.MotionEventModel.Move(
+ distanceMoved = distanceMoved(),
+ )
+ MotionEvent.ACTION_UP ->
+ LongPressHandlingViewInteractionHandler.MotionEventModel.Up(
+ distanceMoved = distanceMoved(),
+ gestureDuration = gestureDuration(),
+ )
+ MotionEvent.ACTION_CANCEL -> LongPressHandlingViewInteractionHandler.MotionEventModel.Cancel
+ else -> LongPressHandlingViewInteractionHandler.MotionEventModel.Other
+ }
+}
+
+private fun MotionEvent.distanceMoved(): Float {
+ return if (historySize > 0) {
+ sqrt((x - getHistoricalX(0)).pow(2) + (y - getHistoricalY(0)).pow(2))
+ } else {
+ 0f
+ }
+}
+
+private fun MotionEvent.gestureDuration(): Long {
+ return eventTime - downTime
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
new file mode 100644
index 0000000..c2d4d12
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.common.ui.view
+
+import android.view.ViewConfiguration
+import kotlinx.coroutines.DisposableHandle
+
+/** Encapsulates logic to handle complex touch interactions with a [LongPressHandlingView]. */
+class LongPressHandlingViewInteractionHandler(
+ /**
+ * Callback to run the given [Runnable] with the given delay, returning a [DisposableHandle]
+ * allowing the delayed runnable to be canceled before it is run.
+ */
+ private val postDelayed: (block: Runnable, delayMs: Long) -> DisposableHandle,
+ /** Callback to be queried to check if the view is attached to its window. */
+ private val isAttachedToWindow: () -> Boolean,
+ /** Callback reporting the a long-press gesture was detected at the given coordinates. */
+ private val onLongPressDetected: (x: Int, y: Int) -> Unit,
+ /** Callback reporting the a single tap gesture was detected at the given coordinates. */
+ private val onSingleTapDetected: () -> Unit,
+) {
+ sealed class MotionEventModel {
+ object Other : MotionEventModel()
+
+ data class Down(
+ val x: Int,
+ val y: Int,
+ ) : MotionEventModel()
+
+ data class Move(
+ val distanceMoved: Float,
+ ) : MotionEventModel()
+
+ data class Up(
+ val distanceMoved: Float,
+ val gestureDuration: Long,
+ ) : MotionEventModel()
+
+ object Cancel : MotionEventModel()
+ }
+
+ var isLongPressHandlingEnabled: Boolean = false
+ var scheduledLongPressHandle: DisposableHandle? = null
+
+ fun onTouchEvent(event: MotionEventModel?): Boolean {
+ if (!isLongPressHandlingEnabled) {
+ return false
+ }
+
+ return when (event) {
+ is MotionEventModel.Down -> {
+ scheduleLongPress(event.x, event.y)
+ true
+ }
+ is MotionEventModel.Move -> {
+ if (event.distanceMoved > ViewConfiguration.getTouchSlop()) {
+ cancelScheduledLongPress()
+ }
+ false
+ }
+ is MotionEventModel.Up -> {
+ cancelScheduledLongPress()
+ if (
+ event.distanceMoved <= ViewConfiguration.getTouchSlop() &&
+ event.gestureDuration < ViewConfiguration.getLongPressTimeout()
+ ) {
+ dispatchSingleTap()
+ }
+ false
+ }
+ is MotionEventModel.Cancel -> {
+ cancelScheduledLongPress()
+ false
+ }
+ else -> false
+ }
+ }
+
+ private fun scheduleLongPress(
+ x: Int,
+ y: Int,
+ ) {
+ scheduledLongPressHandle =
+ postDelayed(
+ {
+ dispatchLongPress(
+ x = x,
+ y = y,
+ )
+ },
+ ViewConfiguration.getLongPressTimeout().toLong(),
+ )
+ }
+
+ private fun dispatchLongPress(
+ x: Int,
+ y: Int,
+ ) {
+ if (!isAttachedToWindow()) {
+ return
+ }
+
+ onLongPressDetected(x, y)
+ }
+
+ private fun cancelScheduledLongPress() {
+ scheduledLongPressHandle?.dispose()
+ }
+
+ private fun dispatchSingleTap() {
+ if (!isAttachedToWindow()) {
+ return
+ }
+
+ onSingleTapDetected()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index f29f6d0..822190f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -188,6 +188,8 @@
/** See [ControlsUiController.getPreferredSelectedItem]. */
fun getPreferredSelection(): SelectedItem
+ fun setPreferredSelection(selectedItem: SelectedItem)
+
/**
* Bind to a service that provides a Device Controls panel (embedded activity). This will allow
* the app to remain "warm", and reduce latency.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 49771dd..ce82af7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -37,6 +37,7 @@
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.controls.ui.SelectedItem
import com.android.systemui.dagger.SysUISingleton
@@ -63,6 +64,7 @@
private val listingController: ControlsListingController,
private val userFileManager: UserFileManager,
private val userTracker: UserTracker,
+ private val authorizedPanelsRepository: AuthorizedPanelsRepository,
optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
dumpManager: DumpManager,
) : Dumpable, ControlsController {
@@ -251,6 +253,11 @@
private fun resetFavorites() {
Favorites.clear()
Favorites.load(persistenceWrapper.readFavorites())
+ // After loading favorites, add the package names of any apps with favorites to the list
+ // of authorized panels. That way, if the user has previously favorited controls for an app,
+ // that panel will be authorized.
+ authorizedPanelsRepository.addAuthorizedPanels(
+ Favorites.getAllStructures().map { it.componentName.packageName }.toSet())
}
private fun confirmAvailability(): Boolean {
@@ -491,6 +498,7 @@
if (!confirmAvailability()) return
executor.execute {
if (Favorites.addFavorite(componentName, structureName, controlInfo)) {
+ authorizedPanelsRepository.addAuthorizedPanels(setOf(componentName.packageName))
persistenceWrapper.storeFavorites(Favorites.getAllStructures())
}
}
@@ -557,6 +565,10 @@
return uiController.getPreferredSelectedItem(getFavorites())
}
+ override fun setPreferredSelection(selectedItem: SelectedItem) {
+ uiController.updatePreferences(selectedItem)
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("ControlsController state:")
pw.println(" Changing users: $userChanging")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 6d6410d..6af8e73 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -34,6 +34,8 @@
import com.android.systemui.controls.management.ControlsListingControllerImpl
import com.android.systemui.controls.management.ControlsProviderSelectorActivity
import com.android.systemui.controls.management.ControlsRequestDialog
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
+import com.android.systemui.controls.panels.AuthorizedPanelsRepositoryImpl
import com.android.systemui.controls.settings.ControlsSettingsDialogManager
import com.android.systemui.controls.settings.ControlsSettingsDialogManagerImpl
import com.android.systemui.controls.ui.ControlActionCoordinator
@@ -104,6 +106,11 @@
coordinator: ControlActionCoordinatorImpl
): ControlActionCoordinator
+ @Binds
+ abstract fun provideAuthorizedPanelsRepository(
+ repository: AuthorizedPanelsRepositoryImpl
+ ): AuthorizedPanelsRepository
+
@BindsOptionalOf
abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index 753d5ad..3fe0f03 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -45,14 +45,15 @@
* @param onAppSelected a callback to indicate that an app has been selected in the list.
*/
class AppAdapter(
- backgroundExecutor: Executor,
- uiExecutor: Executor,
- lifecycle: Lifecycle,
- controlsListingController: ControlsListingController,
- private val layoutInflater: LayoutInflater,
- private val onAppSelected: (ComponentName?) -> Unit = {},
- private val favoritesRenderer: FavoritesRenderer,
- private val resources: Resources
+ backgroundExecutor: Executor,
+ uiExecutor: Executor,
+ lifecycle: Lifecycle,
+ controlsListingController: ControlsListingController,
+ private val layoutInflater: LayoutInflater,
+ private val onAppSelected: (ControlsServiceInfo) -> Unit = {},
+ private val favoritesRenderer: FavoritesRenderer,
+ private val resources: Resources,
+ private val authorizedPanels: Set<String> = emptySet(),
) : RecyclerView.Adapter<AppAdapter.Holder>() {
private var listOfServices = emptyList<ControlsServiceInfo>()
@@ -64,8 +65,10 @@
val localeComparator = compareBy<ControlsServiceInfo, CharSequence>(collator) {
it.loadLabel() ?: ""
}
- listOfServices = serviceInfos.filter { it.panelActivity == null }
- .sortedWith(localeComparator)
+ // No panel or the panel is not authorized
+ listOfServices = serviceInfos.filter {
+ it.panelActivity == null || it.panelActivity?.packageName !in authorizedPanels
+ }.sortedWith(localeComparator)
uiExecutor.execute(::notifyDataSetChanged)
}
}
@@ -86,8 +89,8 @@
override fun onBindViewHolder(holder: Holder, index: Int) {
holder.bindData(listOfServices[index])
- holder.itemView.setOnClickListener {
- onAppSelected(ComponentName.unflattenFromString(listOfServices[index].key))
+ holder.view.setOnClickListener {
+ onAppSelected(listOfServices[index])
}
}
@@ -95,6 +98,8 @@
* Holder for binding views in the [RecyclerView]-
*/
class Holder(view: View, val favRenderer: FavoritesRenderer) : RecyclerView.ViewHolder(view) {
+ val view: View = itemView
+
private val icon: ImageView = itemView.requireViewById(com.android.internal.R.id.icon)
private val title: TextView = itemView.requireViewById(com.android.internal.R.id.title)
private val favorites: TextView = itemView.requireViewById(R.id.favorites)
@@ -106,7 +111,11 @@
fun bindData(data: ControlsServiceInfo) {
icon.setImageDrawable(data.loadIcon())
title.text = data.loadLabel()
- val text = favRenderer.renderFavoritesForComponent(data.componentName)
+ val text = if (data.panelActivity == null) {
+ favRenderer.renderFavoritesForComponent(data.componentName)
+ } else {
+ null
+ }
favorites.text = text
favorites.visibility = if (text == null) View.GONE else View.VISIBLE
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 90bc5d0..3808e73 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -17,6 +17,7 @@
package com.android.systemui.controls.management
import android.app.ActivityOptions
+import android.app.Dialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -31,12 +32,15 @@
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
+import androidx.annotation.VisibleForTesting
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
+import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.controls.ui.SelectedItem
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.UserTracker
@@ -52,7 +56,8 @@
private val listingController: ControlsListingController,
private val controlsController: ControlsController,
private val userTracker: UserTracker,
- private val uiController: ControlsUiController
+ private val authorizedPanelsRepository: AuthorizedPanelsRepository,
+ private val panelConfirmationDialogFactory: PanelConfirmationDialogFactory
) : ComponentActivity() {
companion object {
@@ -72,6 +77,7 @@
}
}
}
+ private var dialog: Dialog? = null
private val mOnBackInvokedCallback = OnBackInvokedCallback {
if (DEBUG) {
@@ -138,9 +144,11 @@
lifecycle,
listingController,
LayoutInflater.from(this),
- ::launchFavoritingActivity,
+ ::onAppSelected,
FavoritesRenderer(resources, controlsController::countFavoritesForComponent),
- resources).apply {
+ resources,
+ authorizedPanelsRepository.getAuthorizedPanels()
+ ).apply {
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
var hasAnimated = false
override fun onChanged() {
@@ -167,13 +175,35 @@
Log.d(TAG, "Unregistered onBackInvokedCallback")
}
onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback)
+ dialog?.cancel()
+ }
+
+ fun onAppSelected(serviceInfo: ControlsServiceInfo) {
+ dialog?.cancel()
+ if (serviceInfo.panelActivity == null) {
+ launchFavoritingActivity(serviceInfo.componentName)
+ } else {
+ val appName = serviceInfo.loadLabel() ?: ""
+ dialog = panelConfirmationDialogFactory.createConfirmationDialog(this, appName) { ok ->
+ if (ok) {
+ authorizedPanelsRepository.addAuthorizedPanels(
+ setOf(serviceInfo.componentName.packageName)
+ )
+ val selected = SelectedItem.PanelItem(appName, componentName)
+ controlsController.setPreferredSelection(selected)
+ animateExitAndFinish()
+ openControlsOrigin()
+ }
+ dialog = null
+ }.also { it.show() }
+ }
}
/**
* Launch the [ControlsFavoritingActivity] for the specified component.
* @param component a component name for a [ControlsProviderService]
*/
- fun launchFavoritingActivity(component: ComponentName?) {
+ private fun launchFavoritingActivity(component: ComponentName?) {
executor.execute {
component?.let {
val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java)
@@ -194,7 +224,15 @@
super.onDestroy()
}
- private fun animateExitAndFinish() {
+ private fun openControlsOrigin() {
+ startActivity(
+ Intent(applicationContext, ControlsActivity::class.java),
+ ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
+ )
+ }
+
+ @VisibleForTesting
+ internal open fun animateExitAndFinish() {
val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
ControlsAnimations.exitAnimation(
rootView,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
new file mode 100644
index 0000000..6f87aa9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls.management
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Factory to create dialogs for consenting to show app panels for specific apps.
+ *
+ * [internalDialogFactory] is for facilitating testing.
+ */
+class PanelConfirmationDialogFactory(
+ private val internalDialogFactory: (Context) -> SystemUIDialog
+) {
+ @Inject constructor() : this({ SystemUIDialog(it) })
+
+ /**
+ * Creates a dialog to show to the user. [response] will be true if an only if the user responds
+ * affirmatively.
+ */
+ fun createConfirmationDialog(
+ context: Context,
+ appName: CharSequence,
+ response: Consumer<Boolean>
+ ): Dialog {
+ val listener =
+ DialogInterface.OnClickListener { _, which ->
+ response.accept(which == DialogInterface.BUTTON_POSITIVE)
+ }
+ return internalDialogFactory(context).apply {
+ setTitle(this.context.getString(R.string.controls_panel_authorization_title, appName))
+ setMessage(this.context.getString(R.string.controls_panel_authorization, appName))
+ setCanceledOnTouchOutside(true)
+ setOnCancelListener { response.accept(false) }
+ setPositiveButton(R.string.controls_dialog_ok, listener)
+ setNeutralButton(R.string.cancel, listener)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
new file mode 100644
index 0000000..3e672f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls.panels
+
+/**
+ * Repository for keeping track of which packages the panel has authorized to show control panels
+ * (embedded activity).
+ */
+interface AuthorizedPanelsRepository {
+
+ /** A set of package names that the user has previously authorized to show panels. */
+ fun getAuthorizedPanels(): Set<String>
+
+ /** Adds [packageNames] to the set of packages that the user has authorized to show panels. */
+ fun addAuthorizedPanels(packageNames: Set<String>)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
new file mode 100644
index 0000000..f7e43a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls.panels
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.android.systemui.R
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
+import javax.inject.Inject
+
+class AuthorizedPanelsRepositoryImpl
+@Inject
+constructor(
+ private val context: Context,
+ private val userFileManager: UserFileManager,
+ private val userTracker: UserTracker
+) : AuthorizedPanelsRepository {
+
+ override fun getAuthorizedPanels(): Set<String> {
+ return getAuthorizedPanelsInternal(instantiateSharedPrefs())
+ }
+
+ override fun addAuthorizedPanels(packageNames: Set<String>) {
+ addAuthorizedPanelsInternal(instantiateSharedPrefs(), packageNames)
+ }
+
+ private fun getAuthorizedPanelsInternal(sharedPreferences: SharedPreferences): Set<String> {
+ return sharedPreferences.getStringSet(KEY, emptySet())!!
+ }
+
+ private fun addAuthorizedPanelsInternal(
+ sharedPreferences: SharedPreferences,
+ packageNames: Set<String>
+ ) {
+ val currentSet = getAuthorizedPanelsInternal(sharedPreferences)
+ sharedPreferences.edit().putStringSet(KEY, currentSet + packageNames).apply()
+ }
+
+ private fun instantiateSharedPrefs(): SharedPreferences {
+ val sharedPref =
+ userFileManager.getSharedPreferences(
+ DeviceControlsControllerImpl.PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE,
+ userTracker.userId,
+ )
+
+ // If we've never run this (i.e., the key doesn't exist), add the default packages
+ if (sharedPref.getStringSet(KEY, null) == null) {
+ sharedPref
+ .edit()
+ .putStringSet(
+ KEY,
+ context.resources
+ .getStringArray(R.array.config_controlsPreferredPackages)
+ .toSet()
+ )
+ .apply()
+ }
+ return sharedPref
+ }
+
+ companion object {
+ private const val KEY = "authorized_panels"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index f5c5905..c1cec9d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -58,6 +58,8 @@
* This element will be the one that appears when the user first opens the controls activity.
*/
fun getPreferredSelectedItem(structures: List<StructureInfo>): SelectedItem
+
+ fun updatePreferences(selectedItem: SelectedItem)
}
sealed class SelectedItem {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 6289788..9e71bef 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -25,6 +25,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
@@ -38,6 +39,7 @@
import android.view.animation.DecelerateInterpolator
import android.widget.AdapterView
import android.widget.ArrayAdapter
+import android.widget.BaseAdapter
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
@@ -60,10 +62,13 @@
import com.android.systemui.controls.management.ControlsFavoritingActivity
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.globalactions.GlobalActionsPopupMenu
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
@@ -87,6 +92,7 @@
class ControlsUiControllerImpl @Inject constructor (
val controlsController: Lazy<ControlsController>,
val context: Context,
+ private val packageManager: PackageManager,
@Main val uiExecutor: DelayableExecutor,
@Background val bgExecutor: DelayableExecutor,
val controlsListingController: Lazy<ControlsListingController>,
@@ -99,6 +105,8 @@
private val userTracker: UserTracker,
private val taskViewFactory: Optional<TaskViewFactory>,
private val controlsSettingsRepository: ControlsSettingsRepository,
+ private val authorizedPanelsRepository: AuthorizedPanelsRepository,
+ private val featureFlags: FeatureFlags,
dumpManager: DumpManager
) : ControlsUiController, Dumpable {
@@ -108,6 +116,11 @@
private const val PREF_IS_PANEL = "controls_is_panel"
private const val FADE_IN_MILLIS = 200L
+
+ private const val OPEN_APP_ID = 0L
+ private const val ADD_CONTROLS_ID = 1L
+ private const val ADD_APP_ID = 2L
+ private const val EDIT_CONTROLS_ID = 3L
}
private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION
@@ -135,6 +148,9 @@
it.getTitle()
}
+ private var openAppIntent: Intent? = null
+ private var overflowMenuAdapter: BaseAdapter? = null
+
private val onSeedingComplete = Consumer<Boolean> {
accepted ->
if (accepted) {
@@ -160,6 +176,7 @@
): ControlsListingController.ControlsListingCallback {
return object : ControlsListingController.ControlsListingCallback {
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+ val authorizedPanels = authorizedPanelsRepository.getAuthorizedPanels()
val lastItems = serviceInfos.map {
val uid = it.serviceInfo.applicationInfo.uid
@@ -169,7 +186,11 @@
it.loadIcon(),
it.componentName,
uid,
- it.panelActivity
+ if (it.componentName.packageName in authorizedPanels) {
+ it.panelActivity
+ } else {
+ null
+ }
)
}
uiExecutor.execute {
@@ -206,6 +227,8 @@
this.parent = parent
this.onDismiss = onDismiss
this.activityContext = activityContext
+ this.openAppIntent = null
+ this.overflowMenuAdapter = null
hidden = false
retainCache = false
@@ -296,6 +319,12 @@
startTargetedActivity(si, ControlsEditingActivity::class.java)
}
+ private fun startDefaultActivity() {
+ openAppIntent?.let {
+ startActivity(it, animateExtra = false)
+ }
+ }
+
private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) {
val i = Intent(activityContext, klazz)
putIntentExtras(i, si)
@@ -319,9 +348,11 @@
startActivity(i)
}
- private fun startActivity(intent: Intent) {
+ private fun startActivity(intent: Intent, animateExtra: Boolean = true) {
// Force animations when transitioning from a dialog to an activity
- intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
+ if (animateExtra) {
+ intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
+ }
if (keyguardStateController.isShowing()) {
activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
@@ -373,8 +404,31 @@
Log.w(ControlsUiController.TAG, "Not TaskViewFactory to display panel $selectionItem")
}
+ bgExecutor.execute {
+ val intent = Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setPackage(selectionItem.componentName.packageName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+ val intents = packageManager
+ .queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0L))
+ intents.firstOrNull { it.activityInfo.exported }?.let { resolved ->
+ intent.setPackage(null)
+ intent.setComponent(resolved.activityInfo.componentName)
+ openAppIntent = intent
+ parent.post {
+ // This will call show on the PopupWindow in the same thread, so make sure this
+ // happens in the view thread.
+ overflowMenuAdapter?.notifyDataSetChanged()
+ }
+ }
+ }
createDropDown(panelsAndStructures, selectionItem)
- createMenu()
+
+ val currentApps = panelsAndStructures.map { it.componentName }.toSet()
+ val allApps = controlsListingController.get()
+ .getCurrentServices().map { it.componentName }.toSet()
+ createMenu(extraApps = (allApps - currentApps).isNotEmpty())
}
private fun createPanelView(componentName: ComponentName) {
@@ -413,22 +467,41 @@
}
}
- private fun createMenu() {
+ private fun createMenu(extraApps: Boolean) {
val isPanel = selectedItem is SelectedItem.PanelItem
val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure
?: EMPTY_STRUCTURE
+ val newFlows = featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
- val items = if (isPanel) {
- arrayOf(
- context.resources.getString(R.string.controls_menu_add),
- )
- } else {
- arrayOf(
- context.resources.getString(R.string.controls_menu_add),
- context.resources.getString(R.string.controls_menu_edit)
- )
+ val items = buildList {
+ add(OverflowMenuAdapter.MenuItem(
+ context.getText(R.string.controls_open_app),
+ OPEN_APP_ID
+ ))
+ if (newFlows || isPanel) {
+ if (extraApps) {
+ add(OverflowMenuAdapter.MenuItem(
+ context.getText(R.string.controls_menu_add_another_app),
+ ADD_APP_ID
+ ))
+ }
+ } else {
+ add(OverflowMenuAdapter.MenuItem(
+ context.getText(R.string.controls_menu_add),
+ ADD_CONTROLS_ID
+ ))
+ }
+ if (!isPanel) {
+ add(OverflowMenuAdapter.MenuItem(
+ context.getText(R.string.controls_menu_edit),
+ EDIT_CONTROLS_ID
+ ))
+ }
}
- var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items)
+
+ val adapter = OverflowMenuAdapter(context, R.layout.controls_more_item, items) { position ->
+ getItemId(position) != OPEN_APP_ID || openAppIntent != null
+ }
val anchor = parent.requireViewById<ImageView>(R.id.controls_more)
anchor.setOnClickListener(object : View.OnClickListener {
@@ -446,25 +519,21 @@
pos: Int,
id: Long
) {
- when (pos) {
- // 0: Add Control
- 0 -> {
- if (isPanel) {
- startProviderSelectorActivity()
- } else {
- startFavoritingActivity(selectedStructure)
- }
- }
- // 1: Edit controls
- 1 -> startEditingActivity(selectedStructure)
+ when (id) {
+ OPEN_APP_ID -> startDefaultActivity()
+ ADD_APP_ID -> startProviderSelectorActivity()
+ ADD_CONTROLS_ID -> startFavoritingActivity(selectedStructure)
+ EDIT_CONTROLS_ID -> startEditingActivity(selectedStructure)
}
dismiss()
}
})
show()
+ listView?.post { listView?.requestAccessibilityFocus() }
}
}
})
+ overflowMenuAdapter = adapter
}
private fun createDropDown(items: List<SelectionItem>, selected: SelectionItem) {
@@ -526,6 +595,7 @@
}
})
show()
+ listView?.post { listView?.requestAccessibilityFocus() }
}
}
})
@@ -610,12 +680,12 @@
}
}
- private fun updatePreferences(si: SelectedItem) {
+ override fun updatePreferences(selectedItem: SelectedItem) {
sharedPreferences.edit()
- .putString(PREF_COMPONENT, si.componentName.flattenToString())
- .putString(PREF_STRUCTURE_OR_APP_NAME, si.name.toString())
- .putBoolean(PREF_IS_PANEL, si is SelectedItem.PanelItem)
- .commit()
+ .putString(PREF_COMPONENT, selectedItem.componentName.flattenToString())
+ .putString(PREF_STRUCTURE_OR_APP_NAME, selectedItem.name.toString())
+ .putBoolean(PREF_IS_PANEL, selectedItem is SelectedItem.PanelItem)
+ .apply()
}
private fun maybeUpdateSelectedItem(item: SelectionItem): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.kt
new file mode 100644
index 0000000..6b84e36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls.ui
+
+import android.content.Context
+import android.widget.ArrayAdapter
+import androidx.annotation.LayoutRes
+
+open class OverflowMenuAdapter(
+ context: Context,
+ @LayoutRes layoutId: Int,
+ itemsWithIds: List<MenuItem>,
+ private val isEnabledInternal: OverflowMenuAdapter.(Int) -> Boolean
+) : ArrayAdapter<CharSequence>(context, layoutId, itemsWithIds.map(MenuItem::text)) {
+
+ private val ids = itemsWithIds.map(MenuItem::id)
+
+ override fun getItemId(position: Int): Long {
+ return ids[position]
+ }
+
+ override fun isEnabled(position: Int): Boolean {
+ return isEnabledInternal(position)
+ }
+
+ data class MenuItem(val text: CharSequence, val id: Long)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 9cbc64e..c9e5449 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -28,6 +28,7 @@
import com.android.systemui.biometrics.UdfpsOverlay
import com.android.systemui.clipboardoverlay.ClipboardListener
import com.android.systemui.dagger.qualifiers.PerUser
+import com.android.systemui.dreams.DreamMonitor
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.keyboard.KeyboardUI
import com.android.systemui.keyguard.KeyguardViewMediator
@@ -293,4 +294,10 @@
@IntoMap
@ClassKey(StylusUsiPowerStartable::class)
abstract fun bindStylusUsiPowerStartable(sysui: StylusUsiPowerStartable): CoreStartable
+
+ /**Inject into DreamMonitor */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamMonitor::class)
+ abstract fun bindDreamMonitor(sysui: DreamMonitor): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 31cc0bd..c3f24f0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -64,6 +64,7 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.security.data.repository.SecurityRepositoryModule;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.smartspace.dagger.SmartspaceModule;
import com.android.systemui.statusbar.CommandQueue;
@@ -107,6 +108,8 @@
import java.util.Optional;
import java.util.concurrent.Executor;
+import javax.inject.Named;
+
import dagger.Binds;
import dagger.BindsOptionalOf;
import dagger.Module;
@@ -196,8 +199,8 @@
@SysUISingleton
@Provides
- static SysUiState provideSysUiState(DumpManager dumpManager) {
- final SysUiState state = new SysUiState();
+ static SysUiState provideSysUiState(DisplayTracker displayTracker, DumpManager dumpManager) {
+ final SysUiState state = new SysUiState(displayTracker);
dumpManager.registerDumpable(state);
return state;
}
@@ -215,6 +218,10 @@
abstract BcSmartspaceConfigPlugin optionalBcSmartspaceConfigPlugin();
@BindsOptionalOf
+ @Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN)
+ abstract BcSmartspaceDataPlugin optionalWeatherSmartspaceConfigPlugin();
+
+ @BindsOptionalOf
abstract Recents optionalRecents();
@BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
new file mode 100644
index 0000000..102f208
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams;
+
+import android.util.Log;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback;
+import com.android.systemui.dreams.conditions.DreamCondition;
+import com.android.systemui.shared.condition.Monitor;
+
+import javax.inject.Inject;
+
+/**
+ * A {@link CoreStartable} to retain a monitor for tracking dreaming.
+ */
+public class DreamMonitor implements CoreStartable {
+ private static final String TAG = "DreamMonitor";
+
+ // We retain a reference to the monitor so it is not garbage-collected.
+ private final Monitor mConditionMonitor;
+ private final DreamCondition mDreamCondition;
+ private final DreamStatusBarStateCallback mCallback;
+
+
+ @Inject
+ public DreamMonitor(Monitor monitor, DreamCondition dreamCondition,
+ DreamStatusBarStateCallback callback) {
+ mConditionMonitor = monitor;
+ mDreamCondition = dreamCondition;
+ mCallback = callback;
+
+ }
+ @Override
+ public void start() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "started");
+ }
+
+ mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
+ .addCondition(mDreamCondition)
+ .build());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index c882f8a..c3bd5d9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -182,6 +182,18 @@
}
}
+ /**
+ * Ends the dream content and dream overlay animations, if they're currently running.
+ * @see [AnimatorSet.end]
+ */
+ fun endAnimations() {
+ mAnimator =
+ mAnimator?.let {
+ it.end()
+ null
+ }
+ }
+
private fun blurAnimator(
view: View,
fromBlurRadius: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 33c8379..50cfb6a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -37,11 +37,11 @@
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.BlurUtils;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -56,7 +56,6 @@
@DreamOverlayComponent.DreamOverlayScope
public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> {
private final DreamOverlayStatusBarViewController mStatusBarViewController;
- private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final BlurUtils mBlurUtils;
private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
private final DreamOverlayStateController mStateController;
@@ -85,6 +84,22 @@
private long mJitterStartTimeMillis;
private boolean mBouncerAnimating;
+ private boolean mWakingUpFromSwipe;
+
+ private final BouncerlessScrimController mBouncerlessScrimController;
+
+ private final BouncerlessScrimController.Callback mBouncerlessExpansionCallback =
+ new BouncerlessScrimController.Callback() {
+ @Override
+ public void onExpansion(ShadeExpansionChangeEvent event) {
+ updateTransitionState(event.getFraction());
+ }
+
+ @Override
+ public void onWakeup() {
+ mWakingUpFromSwipe = true;
+ }
+ };
private final PrimaryBouncerExpansionCallback
mBouncerExpansionCallback =
@@ -127,13 +142,29 @@
}
};
+ /**
+ * If true, overlay entry animations should be skipped once.
+ *
+ * This is turned on when exiting low light and should be turned off once the entry animations
+ * are skipped once.
+ */
+ private boolean mSkipEntryAnimations;
+
+ private final DreamOverlayStateController.Callback
+ mDreamOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onExitLowLight() {
+ mSkipEntryAnimations = true;
+ }
+ };
+
@Inject
public DreamOverlayContainerViewController(
DreamOverlayContainerView containerView,
ComplicationHostViewController complicationHostViewController,
@Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
DreamOverlayStatusBarViewController statusBarViewController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
BlurUtils blurUtils,
@Main Handler handler,
@Main Resources resources,
@@ -143,15 +174,18 @@
@Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
DreamOverlayAnimationsController animationsController,
- DreamOverlayStateController stateController) {
+ DreamOverlayStateController stateController,
+ BouncerlessScrimController bouncerlessScrimController) {
super(containerView);
mDreamOverlayContentView = contentView;
mStatusBarViewController = statusBarViewController;
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mBlurUtils = blurUtils;
mDreamOverlayAnimationsController = animationsController;
mStateController = stateController;
+ mBouncerlessScrimController = bouncerlessScrimController;
+ mBouncerlessScrimController.addCallback(mBouncerlessExpansionCallback);
+
mComplicationHostViewController = complicationHostViewController;
mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
R.dimen.dream_overlay_y_offset);
@@ -170,6 +204,7 @@
@Override
protected void onInit() {
+ mStateController.addCallback(mDreamOverlayStateCallback);
mStatusBarViewController.init();
mComplicationHostViewController.init();
mDreamOverlayAnimationsController.init(mView);
@@ -177,27 +212,27 @@
@Override
protected void onViewAttached() {
+ mWakingUpFromSwipe = false;
mJitterStartTimeMillis = System.currentTimeMillis();
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
- final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
- if (bouncer != null) {
- bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
- }
mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
// Start dream entry animations. Skip animations for low light clock.
if (!mStateController.isLowLightActive()) {
mDreamOverlayAnimationsController.startEntryAnimations();
+
+ if (mSkipEntryAnimations) {
+ // If we're transitioning from the low light dream back to the user dream, skip the
+ // overlay animations and show immediately.
+ mDreamOverlayAnimationsController.endAnimations();
+ mSkipEntryAnimations = false;
+ }
}
}
@Override
protected void onViewDetached() {
mHandler.removeCallbacks(this::updateBurnInOffsets);
- final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
- if (bouncer != null) {
- bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
- }
mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
mDreamOverlayAnimationsController.cancelAnimations();
@@ -266,6 +301,13 @@
*/
public void wakeUp(@NonNull Runnable onAnimationEnd,
@NonNull DelayableExecutor callbackExecutor) {
+ // When swiping causes wakeup, do not run any animations as the dream should exit as soon
+ // as possible.
+ if (mWakingUpFromSwipe) {
+ onAnimationEnd.run();
+ return;
+ }
+
mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index ccfdd096..2c7ecb1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -83,6 +83,12 @@
*/
default void onAvailableComplicationTypesChanged() {
}
+
+ /**
+ * Called when the low light dream is exiting and transitioning back to the user dream.
+ */
+ default void onExitLowLight() {
+ }
}
private final Executor mExecutor;
@@ -278,6 +284,10 @@
* @param active {@code true} if low light mode is active, {@code false} otherwise.
*/
public void setLowLightActive(boolean active) {
+ if (isLowLightActive() && !active) {
+ // Notify that we're exiting low light only on the transition from active to not active.
+ mCallbacks.forEach(Callback::onExitLowLight);
+ }
modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_LOW_LIGHT_ACTIVE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/callbacks/DreamStatusBarStateCallback.java b/packages/SystemUI/src/com/android/systemui/dreams/callbacks/DreamStatusBarStateCallback.java
new file mode 100644
index 0000000..c8c9470
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/callbacks/DreamStatusBarStateCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.callbacks;
+
+import android.util.Log;
+
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+
+import javax.inject.Inject;
+
+/**
+ * A callback that informs {@link SysuiStatusBarStateController} when the dream state has changed.
+ */
+public class DreamStatusBarStateCallback implements Monitor.Callback {
+ private static final String TAG = "DreamStatusBarCallback";
+
+ private final SysuiStatusBarStateController mStateController;
+
+ @Inject
+ public DreamStatusBarStateCallback(SysuiStatusBarStateController statusBarStateController) {
+ mStateController = statusBarStateController;
+ }
+
+ @Override
+ public void onConditionsChanged(boolean allConditionsMet) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onConditionChanged:" + allConditionsMet);
+ }
+
+ mStateController.setIsDreaming(allConditionsMet);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
new file mode 100644
index 0000000..2befce7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.conditions;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.text.TextUtils;
+
+import com.android.systemui.shared.condition.Condition;
+
+import javax.inject.Inject;
+
+/**
+ * {@link DreamCondition} provides a signal when a dream begins and ends.
+ */
+public class DreamCondition extends Condition {
+ private final Context mContext;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ processIntent(intent);
+ }
+ };
+
+ @Inject
+ public DreamCondition(Context context) {
+ mContext = context;
+ }
+
+ private void processIntent(Intent intent) {
+ // In the case of a non-existent sticky broadcast, ignore when there is no intent.
+ if (intent == null) {
+ return;
+ }
+ if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STARTED)) {
+ updateCondition(true);
+ } else if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STOPPED)) {
+ updateCondition(false);
+ } else {
+ throw new IllegalStateException("unexpected intent:" + intent);
+ }
+ }
+
+ @Override
+ protected void start() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+ final Intent stickyIntent = mContext.registerReceiver(mReceiver, filter);
+ processIntent(stickyIntent);
+ }
+
+ @Override
+ protected void stop() {
+ mContext.unregisterReceiver(mReceiver);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 8770cd1..7d8389a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
import com.android.systemui.dreams.dreamcomplication.dagger.ComplicationComponent;
+import com.android.systemui.dreams.touch.scrim.dagger.ScrimModule;
import dagger.Module;
import dagger.Provides;
@@ -44,6 +45,7 @@
@Module(includes = {
RegisteredComplicationsModule.class,
LowLightDreamModule.class,
+ ScrimModule.class
},
subcomponents = {
ComplicationComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 44207f4..73c2289 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -36,11 +36,12 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.dreams.touch.scrim.ScrimController;
+import com.android.systemui.dreams.touch.scrim.ScrimManager;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.util.Optional;
@@ -78,7 +79,8 @@
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final float mBouncerZoneScreenPercentage;
- private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final ScrimManager mScrimManager;
+ private ScrimController mCurrentScrimController;
private float mCurrentExpansion;
private final Optional<CentralSurfaces> mCentralSurfaces;
@@ -90,6 +92,7 @@
private final DisplayMetrics mDisplayMetrics;
private Boolean mCapture;
+ private Boolean mExpanded;
private boolean mBouncerInitiallyShowing;
@@ -101,6 +104,17 @@
private final UiEventLogger mUiEventLogger;
+ private final ScrimManager.Callback mScrimManagerCallback = new ScrimManager.Callback() {
+ @Override
+ public void onScrimControllerChanged(ScrimController controller) {
+ if (mCurrentScrimController != null) {
+ mCurrentScrimController.reset();
+ }
+
+ mCurrentScrimController = controller;
+ }
+ };
+
private final GestureDetector.OnGestureListener mOnGestureListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
@@ -115,8 +129,10 @@
.orElse(false);
if (mCapture) {
+ // reset expanding
+ mExpanded = false;
// Since the user is dragging the bouncer up, set scrimmed to false.
- mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
+ mCurrentScrimController.show();
}
}
@@ -157,10 +173,10 @@
ShadeExpansionChangeEvent event =
new ShadeExpansionChangeEvent(
/* fraction= */ mCurrentExpansion,
- /* expanded= */ false,
+ /* expanded= */ mExpanded,
/* tracking= */ true,
/* dragDownPxAmount= */ dragDownAmount);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(event);
+ mCurrentScrimController.expand(event);
}
@@ -187,7 +203,7 @@
@Inject
public BouncerSwipeTouchHandler(
DisplayMetrics displayMetrics,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ ScrimManager scrimManager,
Optional<CentralSurfaces> centralSurfaces,
NotificationShadeWindowController notificationShadeWindowController,
ValueAnimatorCreator valueAnimatorCreator,
@@ -200,7 +216,7 @@
UiEventLogger uiEventLogger) {
mDisplayMetrics = displayMetrics;
mCentralSurfaces = centralSurfaces;
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mScrimManager = scrimManager;
mNotificationShadeWindowController = notificationShadeWindowController;
mBouncerZoneScreenPercentage = swipeRegionPercentage;
mFlingAnimationUtils = flingAnimationUtils;
@@ -234,9 +250,12 @@
mTouchSession = session;
mVelocityTracker.clear();
mNotificationShadeWindowController.setForcePluginOpen(true, this);
+ mScrimManager.addCallback(mScrimManagerCallback);
+ mCurrentScrimController = mScrimManager.getCurrentController();
session.registerCallback(() -> {
mVelocityTracker.recycle();
+ mScrimManager.removeCallback(mScrimManagerCallback);
mCapture = null;
mNotificationShadeWindowController.setForcePluginOpen(false, this);
});
@@ -273,9 +292,10 @@
final float velocityVector =
(float) Math.hypot(horizontalVelocity, verticalVelocity);
- final float expansion = flingRevealsOverlay(verticalVelocity, velocityVector)
- ? KeyguardBouncerConstants.EXPANSION_HIDDEN
- : KeyguardBouncerConstants.EXPANSION_VISIBLE;
+ mExpanded = !flingRevealsOverlay(verticalVelocity, velocityVector);
+ final float expansion = mExpanded
+ ? KeyguardBouncerConstants.EXPANSION_VISIBLE
+ : KeyguardBouncerConstants.EXPANSION_HIDDEN;
// Log the swiping up to show Bouncer event.
if (!mBouncerInitiallyShowing
@@ -286,7 +306,7 @@
flingToExpansion(verticalVelocity, expansion);
if (expansion == KeyguardBouncerConstants.EXPANSION_HIDDEN) {
- mStatusBarKeyguardViewManager.reset(false);
+ mCurrentScrimController.reset();
}
break;
default:
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 695b59a..b8b459e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -226,6 +226,15 @@
return;
}
+ // When we stop monitoring touches, we must ensure that all active touch sessions and
+ // descendants informed of the removal so any cleanup for active tracking can proceed.
+ mExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
+ while (touchSession != null) {
+ touchSession.onRemoved();
+ touchSession = touchSession.getPredecessor();
+ }
+ }));
+
mCurrentInputSession.dispose();
mCurrentInputSession = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
index 4382757..e1d0339 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
@@ -21,10 +21,10 @@
import android.os.Looper;
import android.view.Choreographer;
-import android.view.Display;
import android.view.GestureDetector;
import android.view.MotionEvent;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -55,8 +55,9 @@
public InputSession(@Named(INPUT_SESSION_NAME) String sessionName,
InputChannelCompat.InputEventListener inputEventListener,
GestureDetector.OnGestureListener gestureListener,
+ DisplayTracker displayTracker,
@Named(PILFER_ON_GESTURE_CONSUME) boolean pilferOnGestureConsume) {
- mInputMonitor = new InputMonitorCompat(sessionName, Display.DEFAULT_DISPLAY);
+ mInputMonitor = new InputMonitorCompat(sessionName, displayTracker.getDefaultDisplayId());
mGestureDetector = new GestureDetector(gestureListener);
mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
new file mode 100644
index 0000000..f5bbba7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.touch.scrim;
+
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
+import javax.inject.Inject;
+
+/**
+ * Implementation for handling swipe movements on the overlay when the keyguard is present.
+ */
+public class BouncerScrimController implements ScrimController {
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ @Inject
+ BouncerScrimController(StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ }
+
+ @Override
+ public void show() {
+ mStatusBarKeyguardViewManager.showBouncer(false);
+ }
+
+ @Override
+ public void expand(ShadeExpansionChangeEvent event) {
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(event);
+ }
+
+ @Override
+ public void reset() {
+ mStatusBarKeyguardViewManager.reset(false);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java
new file mode 100644
index 0000000..01e4d04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.touch.scrim;
+
+import android.os.PowerManager;
+import android.os.SystemClock;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.unfold.util.CallbackController;
+
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * {@link BouncerlessScrimController} handles scrim progression when no keyguard is set. When
+ * fully expanded, the controller dismisses the dream.
+ */
+@SysUISingleton
+public class BouncerlessScrimController implements ScrimController,
+ CallbackController<BouncerlessScrimController.Callback> {
+ private static final String TAG = "BLScrimController";
+
+ /**
+ * {@link Callback} allows {@link BouncerlessScrimController} clients to be informed of
+ * expansion progression and wakeup
+ */
+ public interface Callback {
+ /**
+ * Invoked when there is a change to the scrim expansion.
+ */
+ void onExpansion(ShadeExpansionChangeEvent event);
+
+ /**
+ * Invoked after {@link BouncerlessScrimController} has started waking up the device.
+ */
+ void onWakeup();
+ }
+
+ private final Executor mExecutor;
+ private final PowerManager mPowerManager;
+
+ @Override
+ public void addCallback(Callback listener) {
+ mExecutor.execute(() -> mCallbacks.add(listener));
+ }
+
+ @Override
+ public void removeCallback(Callback listener) {
+ mExecutor.execute(() -> mCallbacks.remove(listener));
+ }
+
+ private final HashSet<Callback> mCallbacks;
+
+
+ @Inject
+ public BouncerlessScrimController(@Main Executor executor,
+ PowerManager powerManager) {
+ mExecutor = executor;
+ mPowerManager = powerManager;
+ mCallbacks = new HashSet<>();
+ }
+
+ @Override
+ public void expand(ShadeExpansionChangeEvent event) {
+ if (event.getExpanded()) {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:SwipeUp");
+ mExecutor.execute(() -> mCallbacks.forEach(callback -> callback.onWakeup()));
+ } else {
+ mExecutor.execute(() -> mCallbacks.forEach(callback -> callback.onExpansion(event)));
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java
new file mode 100644
index 0000000..61629ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.touch.scrim;
+
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+
+/**
+ * {@link ScrimController} provides an interface for the different consumers of scrolling/expansion
+ * events over the dream.
+ */
+public interface ScrimController {
+ /**
+ * Called at the start of expansion before any expansion amount updates.
+ */
+ default void show() {
+ }
+
+ /**
+ * Called for every expansion update.
+ * @param event {@link ShadeExpansionChangeEvent} detailing the change.
+ */
+ default void expand(ShadeExpansionChangeEvent event) {
+ }
+
+ /**
+ * Called at the end of the movement.
+ */
+ default void reset() {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java
new file mode 100644
index 0000000..0d0dff6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.touch.scrim;
+
+import static com.android.systemui.dreams.touch.scrim.dagger.ScrimModule.BOUNCERLESS_SCRIM_CONTROLLER;
+import static com.android.systemui.dreams.touch.scrim.dagger.ScrimModule.BOUNCER_SCRIM_CONTROLLER;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link ScrimManager} helps manage multiple {@link ScrimController} instances, specifying the
+ * appropriate one to use at the current moment and managing the handoff between controllers.
+ */
+public class ScrimManager {
+ private final ScrimController mBouncerScrimController;
+ private final ScrimController mBouncerlessScrimController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final Executor mExecutor;
+
+ private ScrimController mCurrentController;
+ private final HashSet<Callback> mCallbacks;
+
+ /**
+ * Interface implemented for receiving updates to the active {@link ScrimController}.
+ */
+ public interface Callback {
+ /**
+ * Invoked when the controller changes.
+ * @param controller The currently active {@link ScrimController}.
+ */
+ void onScrimControllerChanged(ScrimController controller);
+ }
+
+ private final KeyguardStateController.Callback mKeyguardStateCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ mExecutor.execute(() -> updateController());
+ }
+ };
+
+ @Inject
+ ScrimManager(@Main Executor executor,
+ @Named(BOUNCER_SCRIM_CONTROLLER) ScrimController bouncerScrimController,
+ @Named(BOUNCERLESS_SCRIM_CONTROLLER)ScrimController bouncerlessScrimController,
+ KeyguardStateController keyguardStateController) {
+ mExecutor = executor;
+ mCallbacks = new HashSet<>();
+ mBouncerlessScrimController = bouncerlessScrimController;
+ mBouncerScrimController = bouncerScrimController;
+ mKeyguardStateController = keyguardStateController;
+
+ mKeyguardStateController.addCallback(mKeyguardStateCallback);
+ updateController();
+ }
+
+ private void updateController() {
+ final ScrimController existingController = mCurrentController;
+ mCurrentController = mKeyguardStateController.canDismissLockScreen()
+ ? mBouncerlessScrimController
+ : mBouncerScrimController;
+
+ if (existingController == mCurrentController) {
+ return;
+ }
+
+ mCallbacks.forEach(callback -> callback.onScrimControllerChanged(mCurrentController));
+ }
+
+ /**
+ * Adds a {@link Callback} to receive future changes to the active {@link ScrimController}.
+ */
+ public void addCallback(Callback callback) {
+ mExecutor.execute(() -> mCallbacks.add(callback));
+ }
+
+ /**
+ * Removes the {@link Callback} from receiving further updates.
+ */
+ public void removeCallback(Callback callback) {
+ mExecutor.execute(() -> mCallbacks.remove(callback));
+ }
+
+ /**
+ * Returns the currently get {@link ScrimController}.
+ * @return
+ */
+ public ScrimController getCurrentController() {
+ return mCurrentController;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java
new file mode 100644
index 0000000..4ad5161
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.touch.scrim.dagger;
+
+import com.android.systemui.dreams.touch.scrim.BouncerScrimController;
+import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
+import com.android.systemui.dreams.touch.scrim.ScrimController;
+
+import dagger.Module;
+import dagger.Provides;
+
+import javax.inject.Named;
+
+/**
+ * Module for scrim related dependencies.
+ */
+@Module
+public interface ScrimModule {
+ String BOUNCERLESS_SCRIM_CONTROLLER = "bouncerless_scrim_controller";
+ String BOUNCER_SCRIM_CONTROLLER = "bouncer_scrim_controller";
+
+ /** */
+ @Provides
+ @Named(BOUNCERLESS_SCRIM_CONTROLLER)
+ static ScrimController providesBouncerlessScrimController(
+ BouncerlessScrimController controller) {
+ return controller;
+ }
+
+ /** */
+ @Provides
+ @Named(BOUNCER_SCRIM_CONTROLLER)
+ static ScrimController providesBouncerScrimController(
+ BouncerScrimController controller) {
+ return controller;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
index b94d781..dc7fc28 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -21,6 +21,7 @@
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.InitializationChecker
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -34,7 +35,8 @@
private val commandRegistry: CommandRegistry,
private val flagCommand: FlagCommand,
private val featureFlags: FeatureFlagsDebug,
- private val broadcastSender: BroadcastSender
+ private val broadcastSender: BroadcastSender,
+ private val initializationChecker: InitializationChecker
) : CoreStartable {
init {
@@ -46,8 +48,11 @@
override fun start() {
featureFlags.init()
commandRegistry.registerCommand(FlagCommand.FLAG_COMMAND) { flagCommand }
- val intent = Intent(FlagManager.ACTION_SYSUI_STARTED)
- broadcastSender.sendBroadcast(intent)
+ if (initializationChecker.initializeComponents()) {
+ // protected broadcast should only be sent for the main process
+ val intent = Intent(FlagManager.ACTION_SYSUI_STARTED)
+ broadcastSender.sendBroadcast(intent)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 7718329..a6977e1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -65,8 +65,7 @@
val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true)
// TODO(b/265804648): Tracking Bug
- @JvmField
- val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi")
+ @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi")
// TODO(b/254512538): Tracking Bug
val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
@@ -104,7 +103,7 @@
unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
- unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+ releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
// TODO(b/263414400): Tracking Bug
@JvmField
@@ -200,8 +199,7 @@
/** A different path for unocclusion transitions back to keyguard */
// TODO(b/262859270): Tracking Bug
- @JvmField
- val UNOCCLUSION_TRANSITION = unreleasedFlag(223, "unocclusion_transition", teamfood = true)
+ @JvmField val UNOCCLUSION_TRANSITION = releasedFlag(223, "unocclusion_transition")
// flag for controlling auto pin confirmation and material u shapes in bouncer
@JvmField
@@ -222,6 +220,15 @@
val WALLPAPER_FULLSCREEN_PREVIEW =
unreleasedFlag(227, "wallpaper_fullscreen_preview", teamfood = true)
+ /** Whether the long-press gesture to open wallpaper picker is enabled. */
+ // TODO(b/266242192): Tracking Bug
+ @JvmField
+ val LOCK_SCREEN_LONG_PRESS_ENABLED =
+ unreleasedFlag(
+ 228,
+ "lock_screen_long_press_enabled",
+ )
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -231,7 +238,6 @@
// TODO(b/254513100): Tracking Bug
val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
releasedFlag(401, "smartspace_shared_element_transition_enabled")
- val SMARTSPACE = resourceBooleanFlag(402, R.bool.flag_smartspace, "smartspace")
// TODO(b/258517050): Clean up after the feature is launched.
@JvmField
@@ -267,10 +273,11 @@
// 600- status bar
// TODO(b/256614753): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
+ val NEW_STATUS_BAR_MOBILE_ICONS =
+ unreleasedFlag(606, "new_status_bar_mobile_icons", teamfood = true)
// TODO(b/256614210): Tracking Bug
- val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon")
+ val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon", teamfood = true)
// TODO(b/256614751): Tracking Bug
val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
@@ -354,6 +361,9 @@
val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE =
unreleasedFlag(912, "media_ttt_dismiss_gesture", teamfood = true)
+ // TODO(b/266157412): Tracking Bug
+ val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
+
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
@@ -435,6 +445,18 @@
teamfood = false
)
+ // TODO(b/198643358): Tracking bug
+ @Keep
+ @JvmField
+ val ENABLE_PIP_SIZE_LARGE_SCREEN =
+ sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = false)
+
+ // TODO(b/265998256): Tracking bug
+ @Keep
+ @JvmField
+ val ENABLE_PIP_APP_ICON_OVERLAY =
+ sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = false)
+
// 1200 - predictive back
@Keep
@JvmField
@@ -479,12 +501,20 @@
val WM_SHADE_ANIMATE_BACK_GESTURE =
unreleasedFlag(1208, "persist.wm.debug.shade_animate_back_gesture", teamfood = true)
+ // TODO(b/265639042): Tracking Bug
+ @JvmField
+ val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM =
+ unreleasedFlag(1209, "persist.wm.debug.predictive_back_qs_dialog_anim", teamfood = true)
+
// 1300 - screenshots
// TODO(b/254513155): Tracking Bug
@JvmField
val SCREENSHOT_WORK_PROFILE_POLICY =
unreleasedFlag(1301, "screenshot_work_profile_policy", teamfood = true)
+ // TODO(b/264916608): Tracking Bug
+ @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata")
+
// 1400 - columbus
// TODO(b/254512756): Tracking Bug
val QUICK_TAP_IN_PCC = releasedFlag(1400, "quick_tap_in_pcc")
@@ -504,12 +534,13 @@
releasedFlag(1600, "a11y_floating_menu_fling_spring_animations")
// 1700 - clipboard
- @JvmField val CLIPBOARD_OVERLAY_REFACTOR = releasedFlag(1700, "clipboard_overlay_refactor")
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
// 1800 - shade container
@JvmField
val LEAVE_SHADE_OPEN_FOR_BUGREPORT = releasedFlag(1800, "leave_shade_open_for_bugreport")
+ // TODO(b/265944639): Tracking Bug
+ @JvmField val DUAL_SHADE = releasedFlag(1801, "dual_shade")
// 1900
@JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
@@ -521,6 +552,10 @@
val APP_PANELS_ALL_APPS_ALLOWED =
releasedFlag(2001, "app_panels_all_apps_allowed", teamfood = true)
+ @JvmField
+ val CONTROLS_MANAGEMENT_NEW_FLOWS =
+ unreleasedFlag(2002, "controls_management_new_flows", teamfood = true)
+
// 2100 - Falsing Manager
@JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
@@ -570,6 +605,5 @@
// 2600 - keyboard shortcut
// TODO(b/259352579): Tracking Bug
- @JvmField
- val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout")
+ @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 482138e..680c504 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -31,10 +31,12 @@
import android.util.Log
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.runBlocking
class CustomizationProvider :
@@ -42,6 +44,7 @@
@Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
@Inject lateinit var previewManager: KeyguardRemotePreviewManager
+ @Inject @Main lateinit var mainDispatcher: CoroutineDispatcher
private lateinit var contextAvailableCallback: ContextAvailableCallback
@@ -138,12 +141,14 @@
selectionArgs: Array<out String>?,
sortOrder: String?,
): Cursor? {
- return when (uriMatcher.match(uri)) {
- MATCH_CODE_ALL_AFFORDANCES -> runBlocking { queryAffordances() }
- MATCH_CODE_ALL_SLOTS -> querySlots()
- MATCH_CODE_ALL_SELECTIONS -> runBlocking { querySelections() }
- MATCH_CODE_ALL_FLAGS -> queryFlags()
- else -> null
+ return runBlocking(mainDispatcher) {
+ when (uriMatcher.match(uri)) {
+ MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
+ MATCH_CODE_ALL_SLOTS -> querySlots()
+ MATCH_CODE_ALL_SELECTIONS -> querySelections()
+ MATCH_CODE_ALL_FLAGS -> queryFlags()
+ else -> null
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 4a3071d..7a891b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -79,6 +78,7 @@
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SystemUIApplication;
+import com.android.systemui.settings.DisplayTracker;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -94,6 +94,7 @@
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
private final ScreenOnCoordinator mScreenOnCoordinator;
private final ShellTransitions mShellTransitions;
+ private final DisplayTracker mDisplayTracker;
private static int newModeToLegacyMode(int newMode) {
switch (newMode) {
@@ -257,12 +258,14 @@
public KeyguardService(KeyguardViewMediator keyguardViewMediator,
KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
ScreenOnCoordinator screenOnCoordinator,
- ShellTransitions shellTransitions) {
+ ShellTransitions shellTransitions,
+ DisplayTracker displayTracker) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
mScreenOnCoordinator = screenOnCoordinator;
mShellTransitions = shellTransitions;
+ mDisplayTracker = displayTracker;
}
@Override
@@ -295,7 +298,7 @@
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE,
unoccludeAnimationAdapter);
ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
- DEFAULT_DISPLAY, definition);
+ mDisplayTracker.getDefaultDisplayId(), definition);
return;
}
Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index f6e6d6b..5a9f775 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -19,6 +19,7 @@
import android.app.StatusBarManager
import android.content.Context
+import android.content.pm.PackageManager
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.camera.CameraGestureHelper
@@ -26,7 +27,6 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.StatusBarState
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -37,6 +37,7 @@
@Inject
constructor(
@Application private val context: Context,
+ private val packageManager: PackageManager,
private val cameraGestureHelper: Lazy<CameraGestureHelper>,
) : KeyguardQuickAffordanceConfig {
@@ -79,6 +80,6 @@
}
private fun isLaunchable(): Boolean {
- return cameraGestureHelper.get().canCameraGestureBeLaunched(StatusBarState.KEYGUARD)
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 08edbc6..b3a9cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -22,14 +22,18 @@
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */
interface DeviceEntryFingerprintAuthRepository {
/** Whether the device entry fingerprint auth is locked out. */
- val isLockedOut: Flow<Boolean>
+ val isLockedOut: StateFlow<Boolean>
}
/**
@@ -44,29 +48,34 @@
@Inject
constructor(
val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ @Application scope: CoroutineScope,
) : DeviceEntryFingerprintAuthRepository {
- override val isLockedOut: Flow<Boolean> = conflatedCallbackFlow {
- val sendLockoutUpdate =
- fun() {
- trySendWithFailureLogging(
- keyguardUpdateMonitor.isFingerprintLockedOut,
- TAG,
- "onLockedOutStateChanged"
- )
- }
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
- if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
- sendLockoutUpdate()
+ override val isLockedOut: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val sendLockoutUpdate =
+ fun() {
+ trySendWithFailureLogging(
+ keyguardUpdateMonitor.isFingerprintLockedOut,
+ TAG,
+ "onLockedOutStateChanged"
+ )
}
- }
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onLockedOutStateChanged(
+ biometricSourceType: BiometricSourceType?
+ ) {
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+ sendLockoutUpdate()
+ }
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ sendLockoutUpdate()
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
- keyguardUpdateMonitor.registerCallback(callback)
- sendLockoutUpdate()
- awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
- }
+ .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
companion object {
const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 61d0214..35ae9b4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -26,7 +26,6 @@
import com.android.systemui.log.dagger.BouncerLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.statusbar.phone.KeyguardBouncer
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index d99af90..db95562 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -148,6 +148,9 @@
/** Source of the most recent biometric unlock, such as fingerprint or face. */
val biometricUnlockSource: Flow<BiometricUnlockSource?>
+ /** Whether quick settings or quick-quick settings is visible. */
+ val isQuickSettingsVisible: Flow<Boolean>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -172,6 +175,9 @@
* Returns whether the keyguard bottom area should be constrained to the top of the lock icon
*/
fun isUdfpsSupported(): Boolean
+
+ /** Sets whether quick settings or quick-quick settings is visible. */
+ fun setQuickSettingsVisible(isVisible: Boolean)
}
/** Encapsulates application state for the keyguard. */
@@ -581,6 +587,9 @@
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
+ private val _isQuickSettingsVisible = MutableStateFlow(false)
+ override val isQuickSettingsVisible: Flow<Boolean> = _isQuickSettingsVisible.asStateFlow()
+
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.value = animate
}
@@ -595,6 +604,10 @@
override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
+ override fun setQuickSettingsVisible(isVisible: Boolean) {
+ _isQuickSettingsVisible.value = isVisible
+ }
+
private fun statusBarStateIntToObject(value: Int): StatusBarState {
return when (value) {
0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 4639597..cc99eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -32,4 +32,9 @@
fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
@Binds fun biometricRepository(impl: BiometricRepositoryImpl): BiometricRepository
+
+ @Binds
+ fun deviceEntryFingerprintAuthRepository(
+ impl: DeviceEntryFingerprintAuthRepositoryImpl
+ ): DeviceEntryFingerprintAuthRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
index 28c0b28..6020ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
@@ -21,6 +21,7 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.BiometricRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer
import com.android.systemui.util.time.SystemClock
@@ -34,6 +35,7 @@
constructor(
private val bouncerRepository: KeyguardBouncerRepository,
private val biometricRepository: BiometricRepository,
+ private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val systemClock: SystemClock,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
featureFlags: FeatureFlags,
@@ -99,7 +101,8 @@
bouncerRepository.isAlternateBouncerUIAvailable.value &&
biometricRepository.isFingerprintEnrolled.value &&
biometricRepository.isStrongBiometricAllowed.value &&
- biometricRepository.isFingerprintEnabledByDevicePolicy.value
+ biometricRepository.isFingerprintEnabledByDevicePolicy.value &&
+ !deviceEntryFingerprintAuthRepository.isLockedOut.value
} else {
legacyAlternateBouncer != null &&
keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 14f918d..b5bcd45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -45,6 +45,27 @@
override fun start() {
listenForGoneToAodOrDozing()
listenForGoneToDreaming()
+ listenForGoneToLockscreen()
+ }
+
+ // Primarily for when the user chooses to lock down the device
+ private fun listenForGoneToLockscreen() {
+ scope.launch {
+ keyguardInteractor.isKeyguardShowing
+ .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (isKeyguardShowing, lastStartedStep) ->
+ if (isKeyguardShowing && lastStartedStep.to == KeyguardState.GONE) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.GONE,
+ KeyguardState.LOCKSCREEN,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
}
private fun listenForGoneToDreaming() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 4cf56fe..3d39da6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -155,6 +155,11 @@
}
}
+ /** Sets whether quick settings or quick-quick settings is visible. */
+ fun setQuickSettingsVisible(isVisible: Boolean) {
+ repository.setQuickSettingsVisible(isVisible)
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
new file mode 100644
index 0000000..6525a13
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.shared.model.Position
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.domain.model.KeyguardSettingsPopupMenuModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+
+/** Business logic for use-cases related to the keyguard long-press feature. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardLongPressInteractor
+@Inject
+constructor(
+ @Application unsafeContext: Context,
+ @Application scope: CoroutineScope,
+ transitionInteractor: KeyguardTransitionInteractor,
+ repository: KeyguardRepository,
+ private val activityStarter: ActivityStarter,
+ private val logger: UiEventLogger,
+ private val featureFlags: FeatureFlags,
+ broadcastDispatcher: BroadcastDispatcher,
+) {
+ private val appContext = unsafeContext.applicationContext
+
+ private val _isLongPressHandlingEnabled: StateFlow<Boolean> =
+ if (isFeatureEnabled()) {
+ combine(
+ transitionInteractor.finishedKeyguardState.map {
+ it == KeyguardState.LOCKSCREEN
+ },
+ repository.isQuickSettingsVisible,
+ ) { isFullyTransitionedToLockScreen, isQuickSettingsVisible ->
+ isFullyTransitionedToLockScreen && !isQuickSettingsVisible
+ }
+ } else {
+ flowOf(false)
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /** Whether the long-press handling feature should be enabled. */
+ val isLongPressHandlingEnabled: Flow<Boolean> = _isLongPressHandlingEnabled
+
+ private val _menu = MutableStateFlow<KeyguardSettingsPopupMenuModel?>(null)
+ /** Model for a menu that should be shown; `null` when no menu should be shown. */
+ val menu: Flow<KeyguardSettingsPopupMenuModel?> =
+ isLongPressHandlingEnabled.flatMapLatest { isEnabled ->
+ if (isEnabled) {
+ _menu
+ } else {
+ flowOf(null)
+ }
+ }
+
+ init {
+ if (isFeatureEnabled()) {
+ broadcastDispatcher
+ .broadcastFlow(
+ IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+ )
+ .onEach { hideMenu() }
+ .launchIn(scope)
+ }
+ }
+
+ /** Notifies that the user has long-pressed on the lock screen. */
+ fun onLongPress(x: Int, y: Int) {
+ if (!_isLongPressHandlingEnabled.value) {
+ return
+ }
+
+ showMenu(
+ x = x,
+ y = y,
+ )
+ }
+
+ private fun isFeatureEnabled(): Boolean {
+ return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
+ featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI)
+ }
+
+ /** Updates application state to ask to show the menu at the given coordinates. */
+ private fun showMenu(
+ x: Int,
+ y: Int,
+ ) {
+ _menu.value =
+ KeyguardSettingsPopupMenuModel(
+ position =
+ Position(
+ x = x,
+ y = y,
+ ),
+ onClicked = {
+ hideMenu()
+ navigateToLockScreenSettings()
+ },
+ onDismissed = { hideMenu() },
+ )
+ logger.log(LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
+ }
+
+ /** Updates application state to ask to hide the menu. */
+ private fun hideMenu() {
+ _menu.value = null
+ }
+
+ /** Opens the wallpaper picker screen after the device is unlocked by the user. */
+ private fun navigateToLockScreenSettings() {
+ logger.log(LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
+ activityStarter.dismissKeyguardThenExecute(
+ /* action= */ {
+ appContext.startActivity(
+ Intent(Intent.ACTION_SET_WALLPAPER).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ appContext
+ .getString(R.string.config_wallpaperPickerPackage)
+ .takeIf { it.isNotEmpty() }
+ ?.let { packageName -> setPackage(packageName) }
+ }
+ )
+ true
+ },
+ /* cancel= */ {},
+ /* afterKeyguardGone= */ true,
+ )
+ }
+
+ enum class LogEvents(
+ private val _id: Int,
+ ) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The lock screen was long-pressed and we showed the settings popup menu.")
+ LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN(1292),
+ @UiEvent(doc = "The lock screen long-press popup menu was clicked.")
+ LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED(1293),
+ ;
+
+ override fun getId() = _id
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt
new file mode 100644
index 0000000..7c61e71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.domain.model
+
+import com.android.systemui.common.shared.model.Position
+
+/** Models a settings popup menu for the lock screen. */
+data class KeyguardSettingsPopupMenuModel(
+ /** Where the menu should be anchored, roughly in screen space. */
+ val position: Position,
+ /** Callback to invoke when the menu gets clicked by the user. */
+ val onClicked: () -> Unit,
+ /** Callback to invoke when the menu gets dismissed by the user. */
+ val onDismissed: () -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
index bb5ac84..8222dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
@@ -25,4 +25,5 @@
*/
const val EXPANSION_HIDDEN = 1f
const val EXPANSION_VISIBLE = 0f
+ const val ALPHA_EXPANSION_THRESHOLD = 0.95f
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index d020529..3319f9d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -49,6 +49,7 @@
import kotlin.math.sqrt
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
@@ -66,6 +67,8 @@
object KeyguardBottomAreaViewBinder {
private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
+ private const val SCALE_SELECTED_BUTTON = 1.23f
+ private const val DIM_ALPHA = 0.3f
/**
* Defines interface for an object that acts as the binding between the view and its view-model.
@@ -161,12 +164,26 @@
ambientIndicationArea?.alpha = alpha
indicationArea.alpha = alpha
- startButton.alpha = alpha
- endButton.alpha = alpha
}
}
launch {
+ updateButtonAlpha(
+ view = startButton,
+ viewModel = viewModel.startButton,
+ alphaFlow = viewModel.alpha,
+ )
+ }
+
+ launch {
+ updateButtonAlpha(
+ view = endButton,
+ viewModel = viewModel.endButton,
+ alphaFlow = viewModel.alpha,
+ )
+ }
+
+ launch {
viewModel.indicationAreaTranslationX.collect { translationX ->
indicationArea.translationX = translationX
ambientIndicationArea?.translationX = translationX
@@ -315,6 +332,11 @@
} else {
null
}
+ view
+ .animate()
+ .scaleX(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+ .scaleY(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+ .start()
view.isClickable = viewModel.isClickable
if (viewModel.isClickable) {
@@ -333,6 +355,17 @@
view.isSelected = viewModel.isSelected
}
+ private suspend fun updateButtonAlpha(
+ view: View,
+ viewModel: Flow<KeyguardQuickAffordanceViewModel>,
+ alphaFlow: Flow<Float>,
+ ) {
+ combine(viewModel.map { it.isDimmed }, alphaFlow) { isDimmed, alpha ->
+ if (isDimmed) DIM_ALPHA else alpha
+ }
+ .collect { view.alpha = it }
+ }
+
private class OnTouchListener(
private val view: View,
private val viewModel: KeyguardQuickAffordanceViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt
new file mode 100644
index 0000000..d85682b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.ui.binder
+
+import android.annotation.SuppressLint
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.widget.PopupWindow
+import com.android.systemui.R
+import com.android.systemui.common.ui.binder.IconViewBinder
+import com.android.systemui.common.ui.binder.TextViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsPopupMenuViewModel
+
+object KeyguardLongPressPopupViewBinder {
+ @SuppressLint("InflateParams") // We don't care that the parent is null.
+ fun createAndShow(
+ container: View,
+ viewModel: KeyguardSettingsPopupMenuViewModel,
+ onDismissed: () -> Unit,
+ ): () -> Unit {
+ val contentView: View =
+ LayoutInflater.from(container.context)
+ .inflate(
+ R.layout.keyguard_settings_popup_menu,
+ null,
+ )
+
+ contentView.setOnClickListener { viewModel.onClicked() }
+ IconViewBinder.bind(
+ icon = viewModel.icon,
+ view = contentView.requireViewById(R.id.icon),
+ )
+ TextViewBinder.bind(
+ view = contentView.requireViewById(R.id.text),
+ viewModel = viewModel.text,
+ )
+
+ val popupWindow =
+ PopupWindow(container.context).apply {
+ windowLayoutType = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
+ setBackgroundDrawable(null)
+ animationStyle = com.android.internal.R.style.Animation_Dialog
+ isOutsideTouchable = true
+ isFocusable = true
+ setContentView(contentView)
+ setOnDismissListener { onDismissed() }
+ contentView.measure(
+ View.MeasureSpec.makeMeasureSpec(
+ 0,
+ View.MeasureSpec.UNSPECIFIED,
+ ),
+ View.MeasureSpec.makeMeasureSpec(
+ 0,
+ View.MeasureSpec.UNSPECIFIED,
+ ),
+ )
+ showAtLocation(
+ container,
+ Gravity.NO_GRAVITY,
+ viewModel.position.x - contentView.measuredWidth / 2,
+ viewModel.position.y -
+ contentView.measuredHeight -
+ container.context.resources.getDimensionPixelSize(
+ R.dimen.keyguard_long_press_settings_popup_vertical_offset
+ ),
+ )
+ }
+
+ return { popupWindow.dismiss() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
new file mode 100644
index 0000000..ef3f242
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.ui.binder
+
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.FalsingManager
+import kotlinx.coroutines.launch
+
+object KeyguardLongPressViewBinder {
+ /**
+ * Drives UI for the lock screen long-press feature.
+ *
+ * @param view The view that listens for long-presses.
+ * @param viewModel The view-model that models the UI state.
+ * @param onSingleTap A callback to invoke when the system decides that there was a single tap.
+ * @param falsingManager [FalsingManager] for making sure the long-press didn't just happen in
+ * the user's pocket.
+ */
+ @JvmStatic
+ fun bind(
+ view: LongPressHandlingView,
+ viewModel: KeyguardLongPressViewModel,
+ onSingleTap: () -> Unit,
+ falsingManager: FalsingManager,
+ ) {
+ view.listener =
+ object : LongPressHandlingView.Listener {
+ override fun onLongPressDetected(view: View, x: Int, y: Int) {
+ if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ viewModel.onLongPress(
+ x = x,
+ y = y,
+ )
+ }
+
+ override fun onSingleTapDetected(view: View) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ onSingleTap()
+ }
+ }
+
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.isLongPressHandlingEnabled.collect { isEnabled ->
+ view.setLongPressHandlingEnabled(isEnabled)
+ }
+ }
+
+ launch {
+ var dismissMenu: (() -> Unit)? = null
+
+ viewModel.menu.collect { menuOrNull ->
+ if (menuOrNull != null) {
+ dismissMenu =
+ KeyguardLongPressPopupViewBinder.createAndShow(
+ container = view,
+ viewModel = menuOrNull,
+ onDismissed = menuOrNull.onDismissed,
+ )
+ } else {
+ dismissMenu?.invoke()
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a5ae8ba5..8808574 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -24,7 +24,6 @@
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.os.IBinder
-import android.view.Gravity
import android.view.LayoutInflater
import android.view.SurfaceControlViewHost
import android.view.View
@@ -65,6 +64,11 @@
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT)
+ private val shouldHighlightSelectedAffordance: Boolean =
+ bundle.getBoolean(
+ KeyguardQuickAffordancePreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
+ false,
+ )
private var host: SurfaceControlViewHost
@@ -82,6 +86,7 @@
bundle.getString(
KeyguardQuickAffordancePreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
),
+ shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
runBlocking(mainDispatcher) {
host =
@@ -154,8 +159,7 @@
bottomAreaView,
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- Gravity.BOTTOM,
+ FrameLayout.LayoutParams.MATCH_PARENT,
),
)
}
@@ -195,7 +199,13 @@
?.events
?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
clockView?.let { parentView.removeView(it) }
- clockView = clockController.clock?.largeClock?.view?.apply { parentView.addView(this) }
+ clockView =
+ clockController.clock?.largeClock?.view?.apply {
+ if (shouldHighlightSelectedAffordance) {
+ alpha = DIM_ALPHA
+ }
+ parentView.addView(this)
+ }
}
companion object {
@@ -203,5 +213,7 @@
private const val KEY_VIEW_WIDTH = "width"
private const val KEY_VIEW_HEIGHT = "height"
private const val KEY_DISPLAY_ID = "display_id"
+
+ private const val DIM_ALPHA = 0.3f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 5d85680..1e3b60c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -45,12 +45,17 @@
private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
private val burnInHelperWrapper: BurnInHelperWrapper,
) {
+ data class PreviewMode(
+ val isInPreviewMode: Boolean = false,
+ val shouldHighlightSelectedAffordance: Boolean = false,
+ )
+
/**
* Whether this view-model instance is powering the preview experience that renders exclusively
* in the wallpaper picker application. This should _always_ be `false` for the real lock screen
* experience.
*/
- private val isInPreviewMode = MutableStateFlow(false)
+ private val previewMode = MutableStateFlow(PreviewMode())
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
@@ -87,8 +92,8 @@
keyguardInteractor.isDozing.map { !it }.distinctUntilChanged()
/** An observable for the alpha level for the entire bottom area. */
val alpha: Flow<Float> =
- isInPreviewMode.flatMapLatest { isInPreviewMode ->
- if (isInPreviewMode) {
+ previewMode.flatMapLatest {
+ if (it.isInPreviewMode) {
flowOf(1f)
} else {
bottomAreaInteractor.alpha.distinctUntilChanged()
@@ -129,9 +134,18 @@
* lock screen.
*
* @param initiallySelectedSlotId The ID of the initial slot to render as the selected one.
+ * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be
+ * highlighted (while all others are dimmed to make the selected one stand out).
*/
- fun enablePreviewMode(initiallySelectedSlotId: String?) {
- isInPreviewMode.value = true
+ fun enablePreviewMode(
+ initiallySelectedSlotId: String?,
+ shouldHighlightSelectedAffordance: Boolean,
+ ) {
+ previewMode.value =
+ PreviewMode(
+ isInPreviewMode = true,
+ shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
+ )
onPreviewSlotSelected(
initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
)
@@ -150,9 +164,9 @@
private fun button(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceViewModel> {
- return isInPreviewMode.flatMapLatest { isInPreviewMode ->
+ return previewMode.flatMapLatest { previewMode ->
combine(
- if (isInPreviewMode) {
+ if (previewMode.isInPreviewMode) {
quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
} else {
quickAffordanceInteractor.quickAffordance(position = position)
@@ -161,11 +175,18 @@
areQuickAffordancesFullyOpaque,
selectedPreviewSlotId,
) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId ->
+ val isSelected = selectedPreviewSlotId == position.toSlotId()
model.toViewModel(
- animateReveal = !isInPreviewMode && animateReveal,
- isClickable = isFullyOpaque && !isInPreviewMode,
+ animateReveal = !previewMode.isInPreviewMode && animateReveal,
+ isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
isSelected =
- (isInPreviewMode && selectedPreviewSlotId == position.toSlotId()),
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ isSelected,
+ isDimmed =
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ !isSelected,
)
}
.distinctUntilChanged()
@@ -176,6 +197,7 @@
animateReveal: Boolean,
isClickable: Boolean,
isSelected: Boolean,
+ isDimmed: Boolean,
): KeyguardQuickAffordanceViewModel {
return when (this) {
is KeyguardQuickAffordanceModel.Visible ->
@@ -194,6 +216,7 @@
isActivated = activationState is ActivationState.Active,
isSelected = isSelected,
useLongPress = quickAffordanceInteractor.useLongPress,
+ isDimmed = isDimmed,
)
is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
new file mode 100644
index 0000000..d896390
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.ui.viewmodel
+
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Models UI state to support the lock screen long-press feature. */
+class KeyguardLongPressViewModel
+@Inject
+constructor(
+ private val interactor: KeyguardLongPressInteractor,
+) {
+
+ /** Whether the long-press handling feature should be enabled. */
+ val isLongPressHandlingEnabled: Flow<Boolean> = interactor.isLongPressHandlingEnabled
+
+ /** View-model for a menu that should be shown; `null` when no menu should be shown. */
+ val menu: Flow<KeyguardSettingsPopupMenuViewModel?> =
+ interactor.menu.map { model ->
+ model?.let {
+ KeyguardSettingsPopupMenuViewModel(
+ icon =
+ Icon.Resource(
+ res = R.drawable.ic_settings,
+ contentDescription = null,
+ ),
+ text =
+ Text.Resource(
+ res = R.string.lock_screen_settings,
+ ),
+ position = model.position,
+ onClicked = model.onClicked,
+ onDismissed = model.onDismissed,
+ )
+ }
+ }
+
+ /** Notifies that the user has long-pressed on the lock screen. */
+ fun onLongPress(
+ x: Int,
+ y: Int,
+ ) {
+ interactor.onLongPress(
+ x = x,
+ y = y,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index cf3a6da..cb68a82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -31,6 +31,7 @@
val isActivated: Boolean = false,
val isSelected: Boolean = false,
val useLongPress: Boolean = false,
+ val isDimmed: Boolean = false,
) {
data class OnClickedParameters(
val configKey: String,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt
new file mode 100644
index 0000000..0571b05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Position
+import com.android.systemui.common.shared.model.Text
+
+/** Models the UI state of a keyguard settings popup menu. */
+data class KeyguardSettingsPopupMenuViewModel(
+ val icon: Icon,
+ val text: Text,
+ /** Where the menu should be anchored, roughly in screen space. */
+ val position: Position,
+ /** Callback to invoke when the menu gets clicked by the user. */
+ val onClicked: () -> Unit,
+ /** Callback to invoke when the menu gets dismissed by the user. */
+ val onDismissed: () -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e3c44d1..f61d135 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -200,28 +200,6 @@
}
/**
- * Provides a logging buffer for logs related to the media tap-to-transfer chip on the sender
- * device. See {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
- */
- @Provides
- @SysUISingleton
- @MediaTttSenderLogBuffer
- public static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
- return factory.create("MediaTttSender", 20);
- }
-
- /**
- * Provides a logging buffer for logs related to the media tap-to-transfer chip on the receiver
- * device. See {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
- */
- @Provides
- @SysUISingleton
- @MediaTttReceiverLogBuffer
- public static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
- return factory.create("MediaTttReceiver", 20);
- }
-
- /**
* Provides a logging buffer for logs related to the media mute-await connections. See
* {@link com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 6a5e725..da2164e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -304,6 +304,7 @@
mediaTimeoutListener.stateCallback = { key: String, state: PlaybackState ->
updateState(key, state)
}
+ mediaTimeoutListener.sessionCallback = { key: String -> onSessionDestroyed(key) }
mediaResumeListener.setManager(this)
mediaDataFilter.mediaDataManager = this
@@ -1292,45 +1293,106 @@
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
- val removed = mediaEntries.remove(key)
- if (useMediaResumption && removed?.resumeAction != null && removed.isLocalSession()) {
- Log.d(TAG, "Not removing $key because resumable")
- // Move to resume key (aka package name) if that key doesn't already exist.
- val resumeAction = getResumeMediaAction(removed.resumeAction!!)
- val updated =
- removed.copy(
- token = null,
- actions = listOf(resumeAction),
- semanticActions = MediaButton(playOrPause = resumeAction),
- actionsToShowInCompact = listOf(0),
- active = false,
- resumption = true,
- isPlaying = false,
- isClearable = true
- )
- val pkg = removed.packageName
- val migrate = mediaEntries.put(pkg, updated) == null
- // Notify listeners of "new" controls when migrating or removed and update when not
- if (migrate) {
- notifyMediaDataLoaded(pkg, key, updated)
- } else {
- // Since packageName is used for the key of the resumption controls, it is
- // possible that another notification has already been reused for the resumption
- // controls of this package. In this case, rather than renaming this player as
- // packageName, just remove it and then send a update to the existing resumption
- // controls.
- notifyMediaDataRemoved(key)
- notifyMediaDataLoaded(pkg, pkg, updated)
- }
- logger.logActiveConvertedToResume(updated.appUid, pkg, updated.instanceId)
- return
- }
- if (removed != null) {
+ val removed = mediaEntries.remove(key) ?: return
+
+ if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) {
+ convertToResumePlayer(removed)
+ } else if (mediaFlags.isRetainingPlayersEnabled()) {
+ handlePossibleRemoval(removed, notificationRemoved = true)
+ } else {
notifyMediaDataRemoved(key)
logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
}
}
+ private fun onSessionDestroyed(key: String) {
+ if (!mediaFlags.isRetainingPlayersEnabled()) return
+
+ if (DEBUG) Log.d(TAG, "session destroyed for $key")
+ val entry = mediaEntries.remove(key) ?: return
+ // Clear token since the session is no longer valid
+ val updated = entry.copy(token = null)
+ handlePossibleRemoval(updated)
+ }
+
+ /**
+ * Convert to resume state if the player is no longer valid and active, then notify listeners
+ * that the data was updated. Does not convert to resume state if the player is still valid, or
+ * if it was removed before becoming inactive. (Assumes that [removed] was removed from
+ * [mediaEntries] before this function was called)
+ */
+ private fun handlePossibleRemoval(removed: MediaData, notificationRemoved: Boolean = false) {
+ val key = removed.notificationKey!!
+ val hasSession = removed.token != null
+ if (hasSession && removed.semanticActions != null) {
+ // The app was using session actions, and the session is still valid: keep player
+ if (DEBUG) Log.d(TAG, "Notification removed but using session actions $key")
+ mediaEntries.put(key, removed)
+ notifyMediaDataLoaded(key, key, removed)
+ } else if (!notificationRemoved && removed.semanticActions == null) {
+ // The app was using notification actions, and notif wasn't removed yet: keep player
+ if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key")
+ mediaEntries.put(key, removed)
+ notifyMediaDataLoaded(key, key, removed)
+ } else if (removed.active) {
+ // This player was still active - it didn't last long enough to time out: remove
+ if (DEBUG) Log.d(TAG, "Removing still-active player $key")
+ notifyMediaDataRemoved(key)
+ logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
+ } else {
+ // Convert to resume
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Notification ($notificationRemoved) and/or session " +
+ "($hasSession) gone for inactive player $key"
+ )
+ }
+ convertToResumePlayer(removed)
+ }
+ }
+
+ /** Set the given [MediaData] as a resume state player and notify listeners */
+ private fun convertToResumePlayer(data: MediaData) {
+ val key = data.notificationKey!!
+ if (DEBUG) Log.d(TAG, "Converting $key to resume")
+ // Move to resume key (aka package name) if that key doesn't already exist.
+ val resumeAction = data.resumeAction?.let { getResumeMediaAction(it) }
+ val actions = resumeAction?.let { listOf(resumeAction) } ?: emptyList()
+ val launcherIntent =
+ context.packageManager.getLaunchIntentForPackage(data.packageName)?.let {
+ PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE)
+ }
+ val updated =
+ data.copy(
+ token = null,
+ actions = actions,
+ semanticActions = MediaButton(playOrPause = resumeAction),
+ actionsToShowInCompact = listOf(0),
+ active = false,
+ resumption = true,
+ isPlaying = false,
+ isClearable = true,
+ clickIntent = launcherIntent,
+ )
+ val pkg = data.packageName
+ val migrate = mediaEntries.put(pkg, updated) == null
+ // Notify listeners of "new" controls when migrating or removed and update when not
+ Log.d(TAG, "migrating? $migrate from $key -> $pkg")
+ if (migrate) {
+ notifyMediaDataLoaded(key = pkg, oldKey = key, info = updated)
+ } else {
+ // Since packageName is used for the key of the resumption controls, it is
+ // possible that another notification has already been reused for the resumption
+ // controls of this package. In this case, rather than renaming this player as
+ // packageName, just remove it and then send a update to the existing resumption
+ // controls.
+ notifyMediaDataRemoved(key)
+ notifyMediaDataLoaded(key = pkg, oldKey = pkg, info = updated)
+ }
+ logger.logActiveConvertedToResume(updated.appUid, pkg, updated.instanceId)
+ }
+
fun setMediaResumptionEnabled(isEnabled: Boolean) {
if (useMediaResumption == isEnabled) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
index 7f5c82f..a898b00 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
@@ -71,6 +71,12 @@
*/
lateinit var stateCallback: (String, PlaybackState) -> Unit
+ /**
+ * Callback representing that the [MediaSession] for an active control has been destroyed
+ * @param key Media control unique identifier
+ */
+ lateinit var sessionCallback: (String) -> Unit
+
init {
statusBarStateController.addCallback(
object : StatusBarStateController.StateListener {
@@ -211,6 +217,7 @@
} else {
// For active controls, if the session is destroyed, clean up everything since we
// will need to recreate it if this key is updated later
+ sessionCallback.invoke(key)
destroy()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 5c65c8b..4827a16 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -230,7 +230,14 @@
fun updateColorScheme(colorScheme: ColorScheme?): Boolean {
var anyChanged = false
- colorTransitions.forEach { anyChanged = it.updateColorScheme(colorScheme) || anyChanged }
+ colorTransitions.forEach {
+ val isChanged = it.updateColorScheme(colorScheme)
+
+ // Ignore changes to colorSeamless, since that is expected when toggling dark mode
+ if (it == colorSeamless) return@forEach
+
+ anyChanged = isChanged || anyChanged
+ }
colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
return anyChanged
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 5bc35ca..ab03930 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -45,4 +45,10 @@
/** Check whether we show explicit indicator on UMO */
fun isExplicitIndicatorEnabled() = featureFlags.isEnabled(Flags.MEDIA_EXPLICIT_INDICATOR)
+
+ /**
+ * If true, keep active media controls for the lifetime of the MediaSession, regardless of
+ * whether the underlying notification was dismissed
+ */
+ fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index bb833df..9ae4577 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -17,8 +17,7 @@
package com.android.systemui.media.dagger;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.log.dagger.MediaTttReceiverLogBuffer;
-import com.android.systemui.log.dagger.MediaTttSenderLogBuffer;
+import com.android.systemui.log.LogBufferFactory;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.MediaHost;
@@ -29,12 +28,9 @@
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger;
-import com.android.systemui.media.taptotransfer.receiver.ChipReceiverInfo;
-import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger;
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger;
+import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer;
import com.android.systemui.plugins.log.LogBuffer;
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo;
import java.util.Optional;
@@ -94,22 +90,22 @@
return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
}
+ /** Provides a logging buffer related to the media tap-to-transfer chip on the sender device. */
@Provides
@SysUISingleton
- @MediaTttSenderLogger
- static MediaTttLogger<ChipbarInfo> providesMediaTttSenderLogger(
- @MediaTttSenderLogBuffer LogBuffer buffer
- ) {
- return new MediaTttLogger<>("Sender", buffer);
+ @MediaTttSenderLogBuffer
+ static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
+ return factory.create("MediaTttSender", 30);
}
+ /**
+ * Provides a logging buffer related to the media tap-to-transfer chip on the receiver device.
+ */
@Provides
@SysUISingleton
- @MediaTttReceiverLogger
- static MediaTttLogger<ChipReceiverInfo> providesMediaTttReceiverLogger(
- @MediaTttReceiverLogBuffer LogBuffer buffer
- ) {
- return new MediaTttLogger<>("Receiver", buffer);
+ @MediaTttReceiverLogBuffer
+ static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
+ return factory.create("MediaTttReceiver", 20);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index a9e1a4d..4803371 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -95,6 +95,7 @@
private RecyclerView mDevicesRecyclerView;
private LinearLayout mDeviceListLayout;
private LinearLayout mCastAppLayout;
+ private LinearLayout mMediaMetadataSectionLayout;
private Button mDoneButton;
private Button mStopButton;
private Button mAppButton;
@@ -240,6 +241,7 @@
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
+ mMediaMetadataSectionLayout = mDialogView.requireViewById(R.id.media_metadata_section);
mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
mDoneButton = mDialogView.requireViewById(R.id.done);
mStopButton = mDialogView.requireViewById(R.id.stop);
@@ -255,21 +257,17 @@
mDevicesRecyclerView.setLayoutManager(mLayoutManager);
mDevicesRecyclerView.setAdapter(mAdapter);
mDevicesRecyclerView.setHasFixedSize(false);
- // Init header icon
- mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
// Init bottom buttons
mDoneButton.setOnClickListener(v -> dismiss());
mStopButton.setOnClickListener(v -> {
mMediaOutputController.releaseSession();
dismiss();
});
- mAppButton.setOnClickListener(v -> {
- mBroadcastSender.closeSystemDialogs();
- if (mMediaOutputController.getAppLaunchIntent() != null) {
- mContext.startActivity(mMediaOutputController.getAppLaunchIntent());
- }
- dismiss();
- });
+ mAppButton.setOnClickListener(v -> mMediaOutputController.tryToLaunchMediaApplication());
+ if (mMediaOutputController.isAdvancedLayoutSupported()) {
+ mMediaMetadataSectionLayout.setOnClickListener(
+ v -> mMediaOutputController.tryToLaunchMediaApplication());
+ }
}
@Override
@@ -560,7 +558,7 @@
@Override
public void dismissDialog() {
- dismiss();
+ mBroadcastSender.closeSystemDialogs();
}
void onHeaderIconClick() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 9cf672b..1587e62 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -382,6 +382,15 @@
return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
}
+ void tryToLaunchMediaApplication() {
+ Intent launchIntent = getAppLaunchIntent();
+ if (launchIntent != null) {
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mCallback.dismissDialog();
+ mContext.startActivity(launchIntent);
+ }
+ }
+
CharSequence getHeaderTitle() {
if (mMediaController != null) {
final MediaMetadata metadata = mMediaController.getMetadata();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
deleted file mode 100644
index 8aef938..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.taptotransfer.common
-
-import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
-import com.android.systemui.temporarydisplay.TemporaryViewLogger
-
-/**
- * A logger for media tap-to-transfer events.
- *
- * @param deviceTypeTag the type of device triggering the logs -- "Sender" or "Receiver".
- *
- * TODO(b/245610654): We should de-couple the sender and receiver loggers, since they're vastly
- * different experiences.
- */
-class MediaTttLogger<T : TemporaryViewInfo>(
- deviceTypeTag: String,
- buffer: LogBuffer
-) : TemporaryViewLogger<T>(buffer, BASE_TAG + deviceTypeTag) {
- /** Logs a change in the chip state for the given [mediaRouteId]. */
- fun logStateChange(stateName: String, mediaRouteId: String, packageName: String?) {
- buffer.log(
- tag,
- LogLevel.DEBUG,
- {
- str1 = stateName
- str2 = mediaRouteId
- str3 = packageName
- },
- { "State changed to $str1 for ID=$str2 package=$str3" }
- )
- }
-
- /**
- * Logs an error in trying to update to [displayState].
- *
- * [displayState] is either a [android.app.StatusBarManager.MediaTransferSenderState] or
- * a [android.app.StatusBarManager.MediaTransferReceiverState].
- */
- fun logStateChangeError(displayState: Int) {
- buffer.log(
- tag,
- LogLevel.ERROR,
- { int1 = displayState },
- { "Cannot display state=$int1; aborting" }
- )
- }
-
- /**
- * Logs an invalid sender state transition error in trying to update to [desiredState].
- *
- * @param currentState the previous state of the chip.
- * @param desiredState the new state of the chip.
- */
- fun logInvalidStateTransitionError(
- currentState: String,
- desiredState: String
- ) {
- buffer.log(
- tag,
- LogLevel.ERROR,
- {
- str1 = currentState
- str2 = desiredState
- },
- { "Cannot display state=$str2 after state=$str1; invalid transition" }
- )
- }
-
- /** Logs that we couldn't find information for [packageName]. */
- fun logPackageNotFound(packageName: String) {
- buffer.log(
- tag,
- LogLevel.DEBUG,
- { str1 = packageName },
- { "Package $str1 could not be found" }
- )
- }
-
- /**
- * Logs that a removal request has been bypassed (ignored).
- *
- * @param removalReason the reason that the chip removal was requested.
- * @param bypassReason the reason that the request was bypassed.
- */
- fun logRemovalBypass(removalReason: String, bypassReason: String) {
- buffer.log(
- tag,
- LogLevel.DEBUG,
- {
- str1 = removalReason
- str2 = bypassReason
- },
- { "Chip removal requested due to $str1; however, removal was ignored because $str2" })
- }
-}
-
-private const val BASE_TAG = "MediaTtt"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
new file mode 100644
index 0000000..0e839c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.taptotransfer.common
+
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+
+/** A helper for logging media tap-to-transfer events. */
+object MediaTttLoggerUtils {
+ fun logStateChange(
+ buffer: LogBuffer,
+ tag: String,
+ stateName: String,
+ mediaRouteId: String,
+ packageName: String?,
+ ) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = stateName
+ str2 = mediaRouteId
+ str3 = packageName
+ },
+ { "State changed to $str1 for ID=$str2 package=$str3" }
+ )
+ }
+
+ fun logStateChangeError(buffer: LogBuffer, tag: String, displayState: Int) {
+ buffer.log(
+ tag,
+ LogLevel.ERROR,
+ { int1 = displayState },
+ { "Cannot display state=$int1; aborting" }
+ )
+ }
+
+ fun logPackageNotFound(buffer: LogBuffer, tag: String, packageName: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ { str1 = packageName },
+ { "Package $str1 could not be found" }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index 066c185..a3ae943 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -25,7 +25,6 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.TintedIcon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
/** Utility methods for media tap-to-transfer. */
class MediaTttUtils {
@@ -43,12 +42,13 @@
* default name and icon if we can't find the app name/icon.
*
* @param appPackageName the package name of the app playing the media.
- * @param logger the logger to use for any errors.
+ * @param onPackageNotFoundException a function run if a
+ * [PackageManager.NameNotFoundException] occurs.
*/
fun getIconInfoFromPackageName(
context: Context,
appPackageName: String?,
- logger: MediaTttLogger<out TemporaryViewInfo>
+ onPackageNotFoundException: () -> Unit,
): IconInfo {
if (appPackageName != null) {
val packageManager = context.packageManager
@@ -70,7 +70,7 @@
isAppIcon = true
)
} catch (e: PackageManager.NameNotFoundException) {
- logger.logPackageNotFound(appPackageName)
+ onPackageNotFoundException.invoke()
}
}
return IconInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 6884370..34bf74fa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -30,6 +30,7 @@
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
+import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import com.android.internal.widget.CachingIconView
import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
@@ -39,7 +40,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttIcon
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -64,7 +64,7 @@
open class MediaTttChipControllerReceiver @Inject constructor(
private val commandQueue: CommandQueue,
context: Context,
- @MediaTttReceiverLogger logger: MediaTttLogger<ChipReceiverInfo>,
+ logger: MediaTttReceiverLogger,
windowManager: WindowManager,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
@@ -78,7 +78,7 @@
wakeLockBuilder: WakeLock.Builder,
systemClock: SystemClock,
private val rippleController: MediaTttReceiverRippleController,
-) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger<ChipReceiverInfo>>(
+) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
context,
logger,
windowManager,
@@ -172,9 +172,10 @@
}
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
- var iconInfo = MediaTttUtils.getIconInfoFromPackageName(
- context, newInfo.routeInfo.clientPackageName, logger
- )
+ val packageName = newInfo.routeInfo.clientPackageName
+ var iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, packageName) {
+ logger.logPackageNotFound(packageName)
+ }
if (newInfo.appNameOverride != null) {
iconInfo = iconInfo.copy(
@@ -198,6 +199,7 @@
val iconView = currentView.getAppIconView()
iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
+ iconView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
TintedIconViewBinder.bind(iconInfo.toTintedIcon(), iconView)
}
@@ -207,8 +209,6 @@
val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
animateViewTranslationAndFade(appIconView, -1 * getTranslationAmount(), 1f)
animateViewTranslationAndFade(iconRippleView, -1 * getTranslationAmount(), 1f)
- // Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
- appIconView.postOnAnimation { view.requestAccessibilityFocus() }
rippleController.expandToInProgressState(rippleView, iconRippleView)
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
index 1570d43..67e464c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.receiver;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -26,8 +26,7 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
+ * A {@link LogBuffer} for receiver logs.
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
index 54fc48d..b0c6257 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
@@ -13,14 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.media.taptotransfer.receiver
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.temporarydisplay.TemporaryViewLogger
+import javax.inject.Inject
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttReceiverLogger
+/** A logger for all events related to the media tap-to-transfer receiver experience. */
+@SysUISingleton
+class MediaTttReceiverLogger
+@Inject
+constructor(
+ @MediaTttReceiverLogBuffer buffer: LogBuffer,
+) : TemporaryViewLogger<ChipReceiverInfo>(buffer, TAG) {
+
+ /** Logs a change in the chip state for the given [mediaRouteId]. */
+ fun logStateChange(
+ stateName: String,
+ mediaRouteId: String,
+ packageName: String?,
+ ) {
+ MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+ }
+
+ /** Logs an error in trying to update to [displayState]. */
+ fun logStateChangeError(@StatusBarManager.MediaTransferReceiverState displayState: Int) {
+ MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+ }
+
+ /** Logs that we couldn't find information for [packageName]. */
+ fun logPackageNotFound(packageName: String) {
+ MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+ }
+
+ companion object {
+ private const val TAG = "MediaTttReceiver"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 902a10a0..89ca5d3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -23,11 +23,12 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.CoreStartable
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -35,6 +36,7 @@
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
+import java.io.PrintWriter
import javax.inject.Inject
/**
@@ -48,14 +50,13 @@
private val chipbarCoordinator: ChipbarCoordinator,
private val commandQueue: CommandQueue,
private val context: Context,
- @MediaTttSenderLogger private val logger: MediaTttLogger<ChipbarInfo>,
+ private val dumpManager: DumpManager,
+ private val logger: MediaTttSenderLogger,
private val mediaTttFlags: MediaTttFlags,
private val uiEventLogger: MediaTttSenderUiEventLogger,
-) : CoreStartable {
+) : CoreStartable, Dumpable {
- private var displayedState: ChipStateSender? = null
// A map to store current chip state per id.
- // TODO(b/265455911): Log whenever we add or remove from the store.
private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
private val commandQueueCallbacks =
@@ -76,6 +77,7 @@
override fun start() {
if (mediaTttFlags.isMediaTttEnabled()) {
commandQueue.addCallback(commandQueueCallbacks)
+ dumpManager.registerNormalDumpable(this)
}
}
@@ -93,11 +95,11 @@
return
}
- val currentState = stateMap[routeInfo.id]
- if (!ChipStateSender.isValidStateTransition(currentState, chipState)) {
+ val currentStateForId: ChipStateSender? = stateMap[routeInfo.id]
+ if (!ChipStateSender.isValidStateTransition(currentStateForId, chipState)) {
// ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
logger.logInvalidStateTransitionError(
- currentState = currentState?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
+ currentState = currentStateForId?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
chipState.name
)
return
@@ -105,30 +107,29 @@
uiEventLogger.logSenderStateChange(chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
- // No need to store the state since it is the default state
- removeIdFromStore(routeInfo.id)
- // Return early if we're not displaying a chip anyway
- val currentDisplayedState = displayedState ?: return
+ // Return early if we're not displaying a chip for this ID anyway
+ if (currentStateForId == null) return
val removalReason = ChipStateSender.FAR_FROM_RECEIVER.name
if (
- currentDisplayedState.transferStatus == TransferStatus.IN_PROGRESS ||
- currentDisplayedState.transferStatus == TransferStatus.SUCCEEDED
+ currentStateForId.transferStatus == TransferStatus.IN_PROGRESS ||
+ currentStateForId.transferStatus == TransferStatus.SUCCEEDED
) {
// Don't remove the chip if we're in progress or succeeded, since the user should
// still be able to see the status of the transfer.
logger.logRemovalBypass(
removalReason,
- bypassReason = "transferStatus=${currentDisplayedState.transferStatus.name}"
+ bypassReason = "transferStatus=${currentStateForId.transferStatus.name}"
)
return
}
- displayedState = null
+ // No need to store the state since it is the default state
+ removeIdFromStore(routeInfo.id, reason = removalReason)
chipbarCoordinator.removeView(routeInfo.id, removalReason)
} else {
stateMap[routeInfo.id] = chipState
- displayedState = chipState
+ logger.logStateMap(stateMap)
chipbarCoordinator.registerListener(displayListener)
chipbarCoordinator.displayView(
createChipbarInfo(
@@ -150,7 +151,7 @@
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
context: Context,
- logger: MediaTttLogger<ChipbarInfo>,
+ logger: MediaTttSenderLogger,
): ChipbarInfo {
val packageName = routeInfo.clientPackageName
val otherDeviceName =
@@ -159,12 +160,14 @@
} else {
routeInfo.name.toString()
}
+ val icon =
+ MediaTttUtils.getIconInfoFromPackageName(context, packageName) {
+ logger.logPackageNotFound(packageName)
+ }
return ChipbarInfo(
// Display the app's icon as the start icon
- startIcon =
- MediaTttUtils.getIconInfoFromPackageName(context, packageName, logger)
- .toTintedIcon(),
+ startIcon = icon.toTintedIcon(),
text = chipStateSender.getChipTextString(context, otherDeviceName),
endItem =
when (chipStateSender.endItem) {
@@ -231,12 +234,19 @@
}
private val displayListener =
- TemporaryViewDisplayController.Listener { id -> removeIdFromStore(id) }
+ TemporaryViewDisplayController.Listener { id, reason -> removeIdFromStore(id, reason) }
- private fun removeIdFromStore(id: String) {
+ private fun removeIdFromStore(id: String, reason: String) {
+ logger.logStateMapRemoval(id, reason)
stateMap.remove(id)
+ logger.logStateMap(stateMap)
if (stateMap.isEmpty()) {
chipbarCoordinator.unregisterListener(displayListener)
}
}
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("Current sender states:")
+ pw.println(stateMap.toString())
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
index bf216c6..a262e97 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.sender;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -26,8 +26,7 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
+ * A {@link LogBuffer} for sender logs.
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
index 4393af9..964a95b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
@@ -13,14 +13,102 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.media.taptotransfer.sender
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import javax.inject.Inject
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttSenderLogger
+/** A logger for all events related to the media tap-to-transfer sender experience. */
+@SysUISingleton
+class MediaTttSenderLogger
+@Inject
+constructor(
+ @MediaTttSenderLogBuffer private val buffer: LogBuffer,
+) {
+ /** Logs a change in the chip state for the given [mediaRouteId]. */
+ fun logStateChange(
+ stateName: String,
+ mediaRouteId: String,
+ packageName: String?,
+ ) {
+ MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+ }
+
+ /** Logs an error in trying to update to [displayState]. */
+ fun logStateChangeError(@StatusBarManager.MediaTransferSenderState displayState: Int) {
+ MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+ }
+
+ /** Logs that we couldn't find information for [packageName]. */
+ fun logPackageNotFound(packageName: String) {
+ MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+ }
+
+ /**
+ * Logs an invalid sender state transition error in trying to update to [desiredState].
+ *
+ * @param currentState the previous state of the chip.
+ * @param desiredState the new state of the chip.
+ */
+ fun logInvalidStateTransitionError(currentState: String, desiredState: String) {
+ buffer.log(
+ TAG,
+ LogLevel.ERROR,
+ {
+ str1 = currentState
+ str2 = desiredState
+ },
+ { "Cannot display state=$str2 after state=$str1; invalid transition" }
+ )
+ }
+
+ /**
+ * Logs that a removal request has been bypassed (ignored).
+ *
+ * @param removalReason the reason that the chip removal was requested.
+ * @param bypassReason the reason that the request was bypassed.
+ */
+ fun logRemovalBypass(removalReason: String, bypassReason: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = removalReason
+ str2 = bypassReason
+ },
+ { "Chip removal requested due to $str1; however, removal was ignored because $str2" }
+ )
+ }
+
+ /** Logs the current contents of the state map. */
+ fun logStateMap(map: Map<String, ChipStateSender>) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = map.toString() },
+ { "Current sender states: $str1" }
+ )
+ }
+
+ /** Logs that [id] has been removed from the state map due to [reason]. */
+ fun logStateMapRemoval(id: String, reason: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = id
+ str2 = reason
+ },
+ { "State removal: id=$str1 reason=$str2" }
+ )
+ }
+
+ companion object {
+ private const val TAG = "MediaTttSender"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 1d86343..1edb837 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -24,9 +24,11 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
+import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
@@ -43,7 +45,6 @@
import dagger.Subcomponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
-import java.lang.IllegalArgumentException
import javax.inject.Qualifier
import javax.inject.Scope
import kotlinx.coroutines.CoroutineScope
@@ -69,9 +70,10 @@
): Activity
}
-/** Scoped values for [MediaProjectionAppSelectorComponent].
- * We create a scope for the activity so certain dependencies like [TaskPreviewSizeProvider]
- * could be reused. */
+/**
+ * Scoped values for [MediaProjectionAppSelectorComponent]. We create a scope for the activity so
+ * certain dependencies like [TaskPreviewSizeProvider] could be reused.
+ */
@Module
interface MediaProjectionAppSelectorModule {
@@ -83,6 +85,10 @@
@Binds
@MediaProjectionAppSelectorScope
+ fun bindRecentTaskLabelLoader(impl: ActivityTaskManagerLabelLoader): RecentTaskLabelLoader
+
+ @Binds
+ @MediaProjectionAppSelectorScope
fun bindRecentTaskListProvider(impl: ShellRecentTaskListProvider): RecentTaskListProvider
@Binds
@@ -125,8 +131,10 @@
activity.intent.extras
?: error("MediaProjectionAppSelectorActivity should be launched with extras")
return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
- ?: error("MediaProjectionAppSelectorActivity should be provided with " +
- "$EXTRA_HOST_APP_USER_HANDLE extra")
+ ?: error(
+ "MediaProjectionAppSelectorActivity should be provided with " +
+ "$EXTRA_HOST_APP_USER_HANDLE extra"
+ )
}
@Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
@@ -146,9 +154,7 @@
/** Generates [MediaProjectionAppSelectorComponent]. */
@Subcomponent.Factory
interface Factory {
- /**
- * Create a factory to inject the activity into the graph
- */
+ /** Create a factory to inject the activity into the graph */
fun create(
@BindsInstance activity: MediaProjectionAppSelectorActivity,
@BindsInstance view: MediaProjectionAppSelectorView,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt
new file mode 100644
index 0000000..eadcb93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.mediaprojection.appselector.data
+
+import android.annotation.UserIdInt
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+interface RecentTaskLabelLoader {
+ suspend fun loadLabel(userId: Int, componentName: ComponentName): CharSequence?
+}
+
+class ActivityTaskManagerLabelLoader
+@Inject
+constructor(
+ @Background private val coroutineDispatcher: CoroutineDispatcher,
+ private val packageManager: PackageManager
+) : RecentTaskLabelLoader {
+
+ override suspend fun loadLabel(
+ @UserIdInt userId: Int,
+ componentName: ComponentName
+ ): CharSequence? =
+ withContext(coroutineDispatcher) {
+ val userHandle = UserHandle(userId)
+ val appInfo =
+ packageManager.getApplicationInfo(
+ componentName.packageName,
+ PackageManager.ApplicationInfoFlags.of(0 /* no flags */)
+ )
+ val label = packageManager.getApplicationLabel(appInfo)
+ return@withContext packageManager.getUserBadgedLabel(label, userHandle)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
index 15cfeee..64f97f2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
@@ -20,11 +20,12 @@
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
-import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.android.systemui.R
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelector
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import dagger.assisted.Assisted
@@ -40,9 +41,10 @@
@Assisted private val root: ViewGroup,
private val iconLoader: AppIconLoader,
private val thumbnailLoader: RecentTaskThumbnailLoader,
+ private val labelLoader: RecentTaskLabelLoader,
private val taskViewSizeProvider: TaskPreviewSizeProvider,
@MediaProjectionAppSelector private val scope: CoroutineScope
-) : RecyclerView.ViewHolder(root), ConfigurationListener, TaskPreviewSizeProvider.TaskPreviewSizeListener {
+) : ViewHolder(root), ConfigurationListener, TaskPreviewSizeProvider.TaskPreviewSizeListener {
val thumbnailView: MediaProjectionTaskView = root.requireViewById(R.id.task_thumbnail)
private val iconView: ImageView = root.requireViewById(R.id.task_icon)
@@ -64,6 +66,10 @@
val icon = iconLoader.loadIcon(task.userId, component)
iconView.setImageDrawable(icon)
}
+ launch {
+ val label = labelLoader.loadLabel(task.userId, component)
+ root.contentDescription = label
+ }
}
launch {
val thumbnail = thumbnailLoader.loadThumbnail(task.taskId)
@@ -88,10 +94,10 @@
private fun updateThumbnailSize() {
thumbnailView.layoutParams =
- thumbnailView.layoutParams.apply {
- width = taskViewSizeProvider.size.width()
- height = taskViewSizeProvider.size.height()
- }
+ thumbnailView.layoutParams.apply {
+ width = taskViewSizeProvider.size.width()
+ height = taskViewSizeProvider.size.height()
+ }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 3ecf154..8d80990 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -16,13 +16,12 @@
package com.android.systemui.model;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import android.annotation.NonNull;
import android.util.Log;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
@@ -39,11 +38,16 @@
private static final String TAG = SysUiState.class.getSimpleName();
public static final boolean DEBUG = false;
+ private final DisplayTracker mDisplayTracker;
private @QuickStepContract.SystemUiStateFlags int mFlags;
private final List<SysUiStateCallback> mCallbacks = new ArrayList<>();
private int mFlagsToSet = 0;
private int mFlagsToClear = 0;
+ public SysUiState(DisplayTracker displayTracker) {
+ mDisplayTracker = displayTracker;
+ }
+
/**
* Add listener to be notified of changes made to SysUI state.
* The callback will also be called as part of this function.
@@ -81,7 +85,7 @@
}
private void updateFlags(int displayId) {
- if (displayId != DEFAULT_DISPLAY) {
+ if (displayId != mDisplayTracker.getDefaultDisplayId()) {
// Ignore non-default displays for now
Log.w(TAG, "Ignoring flag update for display: " + displayId, new Throwable());
return;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 32dee92..79b4b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -25,7 +25,6 @@
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -136,6 +135,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -224,6 +224,7 @@
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
private final UserContextProvider mUserContextProvider;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final DisplayTracker mDisplayTracker;
private final RegionSamplingHelper mRegionSamplingHelper;
private final int mNavColorSampleMargin;
private NavigationBarFrame mFrame;
@@ -473,7 +474,8 @@
@Override
public void onStartedWakingUp() {
notifyScreenStateChanged(true);
- if (isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode)) {
+ if (isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker,
+ mNavBarMode)) {
mRegionSamplingHelper.start(mSamplingBounds);
}
}
@@ -557,7 +559,8 @@
Optional<BackAnimation> backAnimation,
UserContextProvider userContextProvider,
WakefulnessLifecycle wakefulnessLifecycle,
- TaskStackChangeListeners taskStackChangeListeners) {
+ TaskStackChangeListeners taskStackChangeListeners,
+ DisplayTracker displayTracker) {
super(navigationBarView);
mFrame = navigationBarFrame;
mContext = context;
@@ -597,6 +600,7 @@
mUserContextProvider = userContextProvider;
mWakefulnessLifecycle = wakefulnessLifecycle;
mTaskStackChangeListeners = taskStackChangeListeners;
+ mDisplayTracker = displayTracker;
mNavColorSampleMargin = getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
@@ -644,12 +648,14 @@
@Override
public boolean isSamplingEnabled() {
- return isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode);
+ return isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker,
+ mNavBarMode);
}
}, mainExecutor, bgExecutor);
mView.setBackgroundExecutor(bgExecutor);
mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler);
+ mView.setDisplayTracker(mDisplayTracker);
mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
}
@@ -681,7 +687,7 @@
getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
.getRotation()));
mDisplayId = mContext.getDisplayId();
- mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
+ mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId();
// Ensure we try to get currentSysuiState from navBarHelper before command queue callbacks
// start firing, since the latter is source of truth
@@ -1484,7 +1490,7 @@
private void onAccessibilityClick(View v) {
final Display display = v.getDisplay();
mAccessibilityManager.notifyAccessibilityButtonClicked(
- display != null ? display.getDisplayId() : DEFAULT_DISPLAY);
+ display != null ? display.getDisplayId() : mDisplayTracker.getDefaultDisplayId());
}
private boolean onAccessibilityLongClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 046a284..3c17465 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -19,7 +19,6 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
@@ -55,6 +54,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
@@ -87,6 +87,7 @@
private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
private FeatureFlags mFeatureFlags;
private final SecureSettings mSecureSettings;
+ private final DisplayTracker mDisplayTracker;
private final DisplayManager mDisplayManager;
private final TaskbarDelegate mTaskbarDelegate;
private int mNavMode;
@@ -119,12 +120,14 @@
Optional<Pip> pipOptional,
Optional<BackAnimation> backAnimation,
FeatureFlags featureFlags,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings,
+ DisplayTracker displayTracker) {
mContext = context;
mHandler = mainHandler;
mNavigationBarComponentFactory = navigationBarComponentFactory;
mFeatureFlags = featureFlags;
mSecureSettings = secureSettings;
+ mDisplayTracker = displayTracker;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
@@ -296,9 +299,10 @@
// Don't need to create nav bar on the default display if we initialize TaskBar.
final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
&& !initializeTaskbarIfNecessary();
- Display[] displays = mDisplayManager.getDisplays();
+ Display[] displays = mDisplayTracker.getAllDisplays();
for (Display display : displays) {
- if (shouldCreateDefaultNavbar || display.getDisplayId() != DEFAULT_DISPLAY) {
+ if (shouldCreateDefaultNavbar
+ || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
createNavigationBar(display, null /* savedState */, result);
}
}
@@ -317,7 +321,7 @@
}
final int displayId = display.getDisplayId();
- final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
+ final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
// We may show TaskBar on the default display for large screen device. Don't need to create
// navigation bar for this case.
@@ -412,7 +416,7 @@
/** @return {@link NavigationBarView} on the default display. */
public @Nullable NavigationBarView getDefaultNavigationBarView() {
- return getNavigationBarView(DEFAULT_DISPLAY);
+ return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
}
/**
@@ -433,7 +437,8 @@
final NavigationBarView navBarView = getNavigationBarView(displayId);
if (navBarView != null) {
navBarView.showPinningEnterExitToast(entering);
- } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+ } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+ && mTaskbarDelegate.isInitialized()) {
mTaskbarDelegate.showPinningEnterExitToast(entering);
}
}
@@ -442,7 +447,8 @@
final NavigationBarView navBarView = getNavigationBarView(displayId);
if (navBarView != null) {
navBarView.showPinningEscapeToast();
- } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+ } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+ && mTaskbarDelegate.isInitialized()) {
mTaskbarDelegate.showPinningEscapeToast();
}
}
@@ -459,7 +465,7 @@
/** @return {@link NavigationBar} on the default display. */
@Nullable
public NavigationBar getDefaultNavigationBar() {
- return mNavigationBars.get(DEFAULT_DISPLAY);
+ return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 43cf623..20b5032 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -24,7 +24,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.util.SparseArray;
-import android.view.Display;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowManager;
import android.view.View;
@@ -32,6 +31,7 @@
import com.android.systemui.R;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -66,6 +66,7 @@
@org.jetbrains.annotations.NotNull
private final IWindowManager mWindowManagerService;
private final LightBarTransitionsController mLightTransitionsController;
+ private final DisplayTracker mDisplayTracker;
private final boolean mAllowAutoDimWallpaperNotVisible;
private boolean mWallpaperVisible;
@@ -102,12 +103,14 @@
public NavigationBarTransitions(
NavigationBarView view,
IWindowManager windowManagerService,
- LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) {
+ LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
+ DisplayTracker displayTracker) {
super(view, R.drawable.nav_background);
mView = view;
mWindowManagerService = windowManagerService;
mLightTransitionsController = lightBarTransitionsControllerFactory.create(this);
+ mDisplayTracker = displayTracker;
mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
.getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
mDarkIntensityListeners = new ArrayList();
@@ -115,7 +118,7 @@
mWallpaperVisibilityListener = new WallpaperVisibilityListener(this);
try {
mWallpaperVisible = mWindowManagerService.registerWallpaperVisibilityListener(
- mWallpaperVisibilityListener, Display.DEFAULT_DISPLAY);
+ mWallpaperVisibilityListener, mDisplayTracker.getDefaultDisplayId());
} catch (RemoteException e) {
}
mView.addOnLayoutChangeListener(
@@ -141,7 +144,7 @@
public void destroy() {
try {
mWindowManagerService.unregisterWallpaperVisibilityListener(mWallpaperVisibilityListener,
- Display.DEFAULT_DISPLAY);
+ mDisplayTracker.getDefaultDisplayId());
} catch (RemoteException e) {
}
mLightTransitionsController.destroy();
@@ -150,7 +153,10 @@
@Override
public void setAutoDim(boolean autoDim) {
// Ensure we aren't in gestural nav if we are triggering auto dim
- if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) return;
+ if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mDisplayTracker,
+ mNavBarMode)) {
+ return;
+ }
if (mAutoDim == autoDim) return;
mAutoDim = autoDim;
applyLightsOut(true, false);
@@ -234,7 +240,7 @@
@Override
public int getTintAnimationDuration() {
- if (isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) {
+ if (isGesturalModeOnDefaultDisplay(mView.getContext(), mDisplayTracker, mNavBarMode)) {
return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
}
return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 88c4fd5..1a3be8e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -16,13 +16,11 @@
package com.android.systemui.navigationbar;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
@@ -75,13 +73,12 @@
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.shared.rotation.RotationButtonController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -127,6 +124,7 @@
private int mDarkIconColor;
private EdgeBackGestureHandler mEdgeBackGestureHandler;
+ private DisplayTracker mDisplayTracker;
private final DeadZone mDeadZone;
private NavigationBarTransitions mBarTransitions;
@Nullable
@@ -361,6 +359,10 @@
mBgExecutor = bgExecutor;
}
+ public void setDisplayTracker(DisplayTracker displayTracker) {
+ mDisplayTracker = displayTracker;
+ }
+
public void setTouchHandler(Gefingerpoken touchHandler) {
mTouchHandler = touchHandler;
}
@@ -558,7 +560,8 @@
}
public void setBehavior(@Behavior int behavior) {
- mRotationButtonController.onBehaviorChanged(Display.DEFAULT_DISPLAY, behavior);
+ mRotationButtonController.onBehaviorChanged(mDisplayTracker.getDefaultDisplayId(),
+ behavior);
}
@Override
@@ -678,7 +681,7 @@
@VisibleForTesting
boolean isRecentsButtonDisabled() {
return mUseCarModeUi || !isOverviewEnabled()
- || getContext().getDisplayId() != Display.DEFAULT_DISPLAY;
+ || getContext().getDisplayId() != mDisplayTracker.getDefaultDisplayId();
}
private Display getContextDisplay() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 4a07159..de0f9b2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -16,8 +16,6 @@
package com.android.systemui.navigationbar.gestural;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE;
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
@@ -55,9 +53,9 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.MotionEventsHandlerBase;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.statusbar.VibratorHelper;
@@ -291,7 +289,8 @@
Context context,
LatencyTracker latencyTracker,
VibratorHelper vibratorHelper,
- @Background Executor backgroundExecutor) {
+ @Background Executor backgroundExecutor,
+ DisplayTracker displayTracker) {
super(context);
mWindowManager = context.getSystemService(WindowManager.class);
@@ -367,7 +366,7 @@
setVisibility(GONE);
- boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY;
+ boolean isPrimaryDisplay = mContext.getDisplayId() == displayTracker.getDefaultDisplayId();
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 08d1857..6bfe1a0 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -17,10 +17,12 @@
package com.android.systemui.notetask
import android.app.KeyguardManager
+import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.os.UserManager
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.util.kotlin.getOrNull
@@ -57,7 +59,7 @@
* If the keyguard is locked, notes will open as a full screen experience. A locked device has
* no contextual information which let us use the whole screen space available.
*
- * If no in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
+ * If not in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
* collapsed if the notes bubble is already opened.
*
* That will let users open other apps in full screen, and take contextual notes.
@@ -68,16 +70,23 @@
val bubbles = optionalBubbles.getOrNull() ?: return
val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
val userManager = optionalUserManager.getOrNull() ?: return
- val intent = intentResolver.resolveIntent() ?: return
// TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
if (!userManager.isUserUnlocked) return
- if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
- context.startActivity(intent)
- } else {
- // TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
- bubbles.showOrHideAppBubble(intent)
+ val intent = intentResolver.resolveIntent() ?: return
+
+ // TODO(b/266686199): We should handle when app not available. For now, we log.
+ try {
+ if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
+ context.startActivity(intent)
+ } else {
+ bubbles.showOrHideAppBubble(intent)
+ }
+ } catch (e: ActivityNotFoundException) {
+ val message =
+ "Activity not found for action: ${NoteTaskIntentResolver.ACTION_CREATE_NOTE}."
+ Log.e(TAG, message, e)
}
}
@@ -106,6 +115,8 @@
}
companion object {
+ private val TAG = NoteTaskController::class.simpleName.orEmpty()
+
// TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead.
const val NOTE_TASK_KEY_EVENT = 311
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
index 4b10d69..11dc1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
@@ -16,70 +16,39 @@
package com.android.systemui.notetask
-import android.content.ComponentName
+import android.app.role.RoleManager
+import android.content.Context
import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ResolveInfoFlags
-import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
import javax.inject.Inject
-/**
- * Class responsible to query all apps and find one that can handle the [ACTION_CREATE_NOTE]. If
- * found, an [Intent] ready for be launched will be returned. Otherwise, returns null.
- *
- * TODO(b/248274123): should be revisited once the notes role is implemented.
- */
internal class NoteTaskIntentResolver
@Inject
constructor(
- private val packageManager: PackageManager,
+ private val context: Context,
+ private val roleManager: RoleManager,
) {
fun resolveIntent(): Intent? {
- val intent = Intent(ACTION_CREATE_NOTE)
- val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
- val infoList = packageManager.queryIntentActivities(intent, flags)
+ val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, context.user).firstOrNull()
- for (info in infoList) {
- val packageName = info.activityInfo.applicationInfo.packageName ?: continue
- val activityName = resolveActivityNameForNotesAction(packageName) ?: continue
+ if (packageName.isNullOrEmpty()) return null
- return Intent(ACTION_CREATE_NOTE)
- .setPackage(packageName)
- .setComponent(ComponentName(packageName, activityName))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
-
- return null
- }
-
- private fun resolveActivityNameForNotesAction(packageName: String): String? {
- val intent = Intent(ACTION_CREATE_NOTE).setPackage(packageName)
- val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
- val resolveInfo = packageManager.resolveActivity(intent, flags)
-
- val activityInfo = resolveInfo?.activityInfo ?: return null
- if (activityInfo.name.isNullOrBlank()) return null
- if (!activityInfo.exported) return null
- if (!activityInfo.enabled) return null
- if (!activityInfo.showWhenLocked) return null
- if (!activityInfo.turnScreenOn) return null
-
- return activityInfo.name
+ return Intent(ACTION_CREATE_NOTE)
+ .setPackage(packageName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint was
+ // used to start it.
+ .putExtra(INTENT_EXTRA_USE_STYLUS_MODE, true)
}
companion object {
- // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
+ // TODO(b/265912743): Use Intent.ACTION_CREATE_NOTE instead.
const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
// TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
- const val NOTE_ROLE = "android.app.role.NOTES"
+ const val ROLE_NOTES = "android.app.role.NOTES"
+
+ // TODO(b/265912743): Use Intent.INTENT_EXTRA_USE_STYLUS_MODE instead.
+ const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE"
}
}
-
-private val ActivityInfo.showWhenLocked: Boolean
- get() = flags and ActivityInfo.FLAG_SHOW_WHEN_LOCKED != 0
-
-private val ActivityInfo.turnScreenOn: Boolean
- get() = flags and ActivityInfo.FLAG_TURN_SCREEN_ON != 0
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 22ce121..ec6a16a 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -51,7 +51,7 @@
featureFlags: FeatureFlags,
roleManager: RoleManager,
): Boolean {
- val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.NOTE_ROLE)
+ val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.ROLE_NOTES)
val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
return isRoleAvailable && isFeatureEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index b48ea23..be93550 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -43,6 +43,7 @@
import javax.inject.Inject
private const val TAG = "AutoAddTracker"
+private const val DELIMITER = ","
/**
* Class to track tiles that have been auto-added
@@ -67,7 +68,7 @@
@GuardedBy("autoAdded")
private val autoAdded = ArraySet<String>()
- private var restoredTiles: Set<String>? = null
+ private var restoredTiles: Map<String, AutoTile>? = null
override val currentUserId: Int
get() = userId
@@ -98,25 +99,26 @@
when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
Settings.Secure.QS_TILES -> {
restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
- ?.split(",")
- ?.toSet()
+ ?.split(DELIMITER)
+ ?.mapIndexed(::AutoTile)
+ ?.associateBy(AutoTile::tileType)
?: run {
Log.w(TAG, "Null restored tiles for user $userId")
- emptySet()
+ emptyMap()
}
}
Settings.Secure.QS_AUTO_ADDED_TILES -> {
- restoredTiles?.let { tiles ->
+ restoredTiles?.let { restoredTiles ->
val restoredAutoAdded = intent
.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
- ?.split(",")
+ ?.split(DELIMITER)
?: emptyList()
val autoAddedBeforeRestore = intent
.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
- ?.split(",")
+ ?.split(DELIMITER)
?: emptyList()
- val tilesToRemove = restoredAutoAdded.filter { it !in tiles }
+ val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
if (tilesToRemove.isNotEmpty()) {
qsHost.removeTiles(tilesToRemove)
}
@@ -180,6 +182,9 @@
registerBroadcastReceiver()
}
+ fun getRestoredTilePosition(tile: String): Int =
+ restoredTiles?.get(tile)?.index ?: QSTileHost.POSITION_AT_END
+
/**
* Returns `true` if the tile has been auto-added before
*/
@@ -196,12 +201,12 @@
*/
fun setTileAdded(tile: String) {
val tiles = synchronized(autoAdded) {
- if (autoAdded.add(tile)) {
- getTilesFromListLocked()
- } else {
- null
- }
+ if (autoAdded.add(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
}
+ }
tiles?.let { saveTiles(it) }
}
@@ -222,7 +227,7 @@
}
private fun getTilesFromListLocked(): String {
- return TextUtils.join(",", autoAdded)
+ return TextUtils.join(DELIMITER, autoAdded)
}
private fun saveTiles(tiles: String) {
@@ -245,7 +250,7 @@
private fun getAdded(): Collection<String> {
val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
- return current?.split(",") ?: emptySet()
+ return current?.split(DELIMITER) ?: emptySet()
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -281,4 +286,6 @@
)
}
}
+
+ private data class AutoTile(val index: Int, val tileType: String)
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index d1cf46c..464b6e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -636,7 +636,8 @@
isRunning: Boolean
) {
synchronized(lock) {
- val userPackageKey = UserPackage(summary.sourceUserId, summary.sourcePackageName)
+ val userPackageKey = UserPackage(
+ UserHandle.getUserId(summary.callingUid), summary.callingPackageName)
if (isRunning) {
runningTaskIdentifiers
.getOrPut(userPackageKey) { StartTimeAndIdentifiers(systemClock) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index cfda9fd..7c2536d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.qs.external;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
import android.app.PendingIntent;
@@ -63,6 +62,7 @@
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.DisplayTracker;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -90,6 +90,7 @@
private final TileServiceManager mServiceManager;
private final int mUser;
private final CustomTileStatePersister mCustomTileStatePersister;
+ private final DisplayTracker mDisplayTracker;
@Nullable
private android.graphics.drawable.Icon mDefaultIcon;
@Nullable
@@ -120,7 +121,8 @@
String action,
Context userContext,
CustomTileStatePersister customTileStatePersister,
- TileServices tileServices
+ TileServices tileServices,
+ DisplayTracker displayTracker
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -135,6 +137,7 @@
mServiceManager = tileServices.getTileWrapper(this);
mService = mServiceManager.getTileService();
mCustomTileStatePersister = customTileStatePersister;
+ mDisplayTracker = displayTracker;
}
@Override
@@ -310,7 +313,7 @@
mIsShowingDialog = false;
try {
if (DEBUG) Log.d(TAG, "Removing token");
- mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
+ mWindowManager.removeWindowToken(mToken, mDisplayTracker.getDefaultDisplayId());
} catch (RemoteException e) {
}
}
@@ -335,7 +338,8 @@
if (mIsTokenGranted && !mIsShowingDialog) {
try {
if (DEBUG) Log.d(TAG, "Removing token");
- mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
+ mWindowManager.removeWindowToken(mToken,
+ mDisplayTracker.getDefaultDisplayId());
} catch (RemoteException e) {
}
mIsTokenGranted = false;
@@ -354,7 +358,7 @@
if (mIsTokenGranted) {
try {
if (DEBUG) Log.d(TAG, "Removing token");
- mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
+ mWindowManager.removeWindowToken(mToken, mDisplayTracker.getDefaultDisplayId());
} catch (RemoteException e) {
}
}
@@ -398,8 +402,8 @@
mViewClicked = view;
try {
if (DEBUG) Log.d(TAG, "Adding token");
- mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY,
- null /* options */);
+ mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG,
+ mDisplayTracker.getDefaultDisplayId(), null /* options */);
mIsTokenGranted = true;
} catch (RemoteException e) {
}
@@ -566,6 +570,7 @@
final QSLogger mQSLogger;
final CustomTileStatePersister mCustomTileStatePersister;
private TileServices mTileServices;
+ final DisplayTracker mDisplayTracker;
Context mUserContext;
String mSpec = "";
@@ -581,7 +586,8 @@
ActivityStarter activityStarter,
QSLogger qsLogger,
CustomTileStatePersister customTileStatePersister,
- TileServices tileServices
+ TileServices tileServices,
+ DisplayTracker displayTracker
) {
mQSHostLazy = hostLazy;
mBackgroundLooper = backgroundLooper;
@@ -593,6 +599,7 @@
mQSLogger = qsLogger;
mCustomTileStatePersister = customTileStatePersister;
mTileServices = tileServices;
+ mDisplayTracker = displayTracker;
}
Builder setSpec(@NonNull String spec) {
@@ -623,7 +630,8 @@
action,
mUserContext,
mCustomTileStatePersister,
- mTileServices
+ mTileServices,
+ mDisplayTracker
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1151475..a979e5a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
@@ -30,6 +29,7 @@
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -88,6 +88,7 @@
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -144,6 +145,7 @@
private final UserTracker mUserTracker;
private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
private final UiEventLogger mUiEventLogger;
+ private final DisplayTracker mDisplayTracker;
private Region mActiveNavBarRegion;
private SurfaceControl mNavigationBarSurface;
@@ -225,11 +227,11 @@
@Override
public void onImeSwitcherPressed() {
- // TODO(b/204901476) We're intentionally using DEFAULT_DISPLAY for now since
+ // TODO(b/204901476) We're intentionally using the default display for now since
// Launcher/Taskbar isn't display aware.
mContext.getSystemService(InputMethodManager.class)
.showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
- DEFAULT_DISPLAY);
+ mDisplayTracker.getDefaultDisplayId());
mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
}
@@ -507,6 +509,7 @@
UserTracker userTracker,
ScreenLifecycle screenLifecycle,
UiEventLogger uiEventLogger,
+ DisplayTracker displayTracker,
KeyguardUnlockAnimationController sysuiUnlockAnimationController,
AssistUtils assistUtils,
DumpManager dumpManager) {
@@ -534,6 +537,7 @@
mSysUiState = sysUiState;
mSysUiState.addCallback(this::notifySystemUiStateFlags);
mUiEventLogger = uiEventLogger;
+ mDisplayTracker = displayTracker;
dumpManager.registerDumpable(getClass().getSimpleName(), this);
@@ -652,13 +656,14 @@
}
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
- boolean bouncerShowing, boolean isDozing, boolean panelExpanded) {
+ boolean bouncerShowing, boolean isDozing, boolean panelExpanded, boolean isDreaming) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded)
.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
keyguardShowing && keyguardOccluded)
.setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
.setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
+ .setFlag(SYSUI_STATE_DEVICE_DREAMING, isDreaming)
.commitUpdate(mContext.getDisplayId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
index 017e57f..310baaf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
@@ -25,8 +25,22 @@
import com.android.systemui.R
object ActionIntentCreator {
+ /** @return a chooser intent to share the given URI. */
+ fun createShareIntent(uri: Uri) = createShareIntent(uri, null, null)
+
/** @return a chooser intent to share the given URI with the optional provided subject. */
- fun createShareIntent(uri: Uri, subject: String?): Intent {
+ fun createShareIntentWithSubject(uri: Uri, subject: String?) =
+ createShareIntent(uri, subject = subject)
+
+ /** @return a chooser intent to share the given URI with the optional provided extra text. */
+ fun createShareIntentWithExtraText(uri: Uri, extraText: String?) =
+ createShareIntent(uri, extraText = extraText)
+
+ private fun createShareIntent(
+ uri: Uri,
+ subject: String? = null,
+ extraText: String? = null
+ ): Intent {
// Create a share intent, this will always go through the chooser activity first
// which should not trigger auto-enter PiP
val sharingIntent =
@@ -43,6 +57,7 @@
)
putExtra(Intent.EXTRA_SUBJECT, subject)
+ putExtra(Intent.EXTRA_TEXT, extraText)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 01e32b7a..aa8e2c0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -22,7 +22,6 @@
import android.os.RemoteException
import android.os.UserHandle
import android.util.Log
-import android.view.Display
import android.view.IRemoteAnimationFinishedCallback
import android.view.IRemoteAnimationRunner
import android.view.RemoteAnimationAdapter
@@ -33,6 +32,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.DisplayTracker
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
@@ -47,6 +47,7 @@
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
private val context: Context,
+ private val displayTracker: DisplayTracker
) {
/**
* Execute the given intent with startActivity while performing operations for screenshot action
@@ -82,7 +83,7 @@
val runner = RemoteAnimationAdapter(SCREENSHOT_REMOTE_RUNNER, 0, 0)
try {
WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionRemote(runner, Display.DEFAULT_DISPLAY)
+ .overridePendingAppTransitionRemote(runner, displayTracker.defaultDisplayId)
} catch (e: Exception) {
Log.e(TAG, "Error overriding screenshot app transition", e)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index 814b8e9..4f5cb72 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -16,8 +16,6 @@
package com.android.systemui.screenshot;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_EDIT;
import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
@@ -35,6 +33,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.WindowManagerGlobal;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -52,14 +51,17 @@
private final CentralSurfaces mCentralSurfaces;
private final ActivityManagerWrapper mActivityManagerWrapper;
private final ScreenshotSmartActions mScreenshotSmartActions;
+ private final DisplayTracker mDisplayTracker;
@Inject
public ActionProxyReceiver(Optional<CentralSurfaces> centralSurfacesOptional,
ActivityManagerWrapper activityManagerWrapper,
- ScreenshotSmartActions screenshotSmartActions) {
+ ScreenshotSmartActions screenshotSmartActions,
+ DisplayTracker displayTracker) {
mCentralSurfaces = centralSurfacesOptional.orElse(null);
mActivityManagerWrapper = activityManagerWrapper;
mScreenshotSmartActions = screenshotSmartActions;
+ mDisplayTracker = displayTracker;
}
@Override
@@ -78,7 +80,8 @@
ScreenshotController.SCREENSHOT_REMOTE_RUNNER, 0, 0);
try {
WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
+ .overridePendingAppTransitionRemote(runner,
+ mDisplayTracker.getDefaultDisplayId());
} catch (Exception e) {
Log.e(TAG, "Error overriding screenshot app transition", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
new file mode 100644
index 0000000..ab8fc65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.screenshot;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.IAssistDataReceiver;
+import android.app.assist.AssistContent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Can be used to request the AssistContent from a provided task id, useful for getting the web uri
+ * if provided from the task.
+ *
+ * Forked from
+ * packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
+ */
+@SysUISingleton
+public class AssistContentRequester {
+ private static final String TAG = "AssistContentRequester";
+ private static final String ASSIST_KEY_CONTENT = "content";
+
+ /** For receiving content, called on the main thread. */
+ public interface Callback {
+ /**
+ * Called when the {@link android.app.assist.AssistContent} of the requested task is
+ * available.
+ **/
+ void onAssistContentAvailable(AssistContent assistContent);
+ }
+
+ private final IActivityTaskManager mActivityTaskManager;
+ private final String mPackageName;
+ private final Executor mCallbackExecutor;
+ private final Executor mSystemInteractionExecutor;
+ private final String mAttributionTag;
+
+ // If system loses the callback, our internal cache of original callback will also get cleared.
+ private final Map<Object, Callback> mPendingCallbacks =
+ Collections.synchronizedMap(new WeakHashMap<>());
+
+ @Inject
+ public AssistContentRequester(Context context, @Main Executor mainExecutor,
+ @Background Executor bgExecutor) {
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mPackageName = context.getApplicationContext().getPackageName();
+ mCallbackExecutor = mainExecutor;
+ mSystemInteractionExecutor = bgExecutor;
+ mAttributionTag = context.getAttributionTag();
+ }
+
+ /**
+ * Request the {@link AssistContent} from the task with the provided id.
+ *
+ * @param taskId to query for the content.
+ * @param callback to call when the content is available, called on the main thread.
+ */
+ public void requestAssistContent(final int taskId, final Callback callback) {
+ // ActivityTaskManager interaction here is synchronous, so call off the main thread.
+ mSystemInteractionExecutor.execute(() -> {
+ try {
+ mActivityTaskManager.requestAssistDataForTask(
+ new AssistDataReceiver(callback, this), taskId, mPackageName,
+ mAttributionTag);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Requesting assist content failed for task: " + taskId, e);
+ }
+ });
+ }
+
+ private void executeOnMainExecutor(Runnable callback) {
+ mCallbackExecutor.execute(callback);
+ }
+
+ private static final class AssistDataReceiver extends IAssistDataReceiver.Stub {
+
+ // The AssistDataReceiver binder callback object is passed to a system server, that may
+ // keep hold of it for longer than the lifetime of the AssistContentRequester object,
+ // potentially causing a memory leak. In the callback passed to the system server, only
+ // keep a weak reference to the parent object and lookup its callback if it still exists.
+ private final WeakReference<AssistContentRequester> mParentRef;
+ private final Object mCallbackKey = new Object();
+
+ AssistDataReceiver(Callback callback, AssistContentRequester parent) {
+ parent.mPendingCallbacks.put(mCallbackKey, callback);
+ mParentRef = new WeakReference<>(parent);
+ }
+
+ @Override
+ public void onHandleAssistData(Bundle data) {
+ if (data == null) {
+ return;
+ }
+
+ final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
+ if (content == null) {
+ Log.e(TAG, "Received AssistData, but no AssistContent found");
+ return;
+ }
+
+ AssistContentRequester requester = mParentRef.get();
+ if (requester != null) {
+ Callback callback = requester.mPendingCallbacks.get(mCallbackKey);
+ if (callback != null) {
+ requester.executeOnMainExecutor(
+ () -> callback.onAssistContentAvailable(content));
+ } else {
+ Log.d(TAG, "Callback received after calling UI was disposed of");
+ }
+ } else {
+ Log.d(TAG, "Callback received after Requester was collected");
+ }
+ }
+
+ @Override
+ public void onHandleAssistScreenshot(Bitmap screenshot) {}
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index d64b33b..ca8e101 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -366,7 +366,7 @@
private void doShare(Uri uri) {
if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
- Intent shareIntent = ActionIntentCreator.INSTANCE.createShareIntent(uri, null);
+ Intent shareIntent = ActionIntentCreator.INSTANCE.createShareIntent(uri);
mActionExecutor.launchIntentAsync(shareIntent, null,
mScreenshotUserHandle.getIdentifier(), false);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index f011aab..4db48ac 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -45,6 +45,7 @@
*
* @param request the request to process
*/
+ // TODO: Delete once SCREENSHOT_METADATA flag is launched
suspend fun process(request: ScreenshotRequest): ScreenshotRequest {
var result = request
@@ -93,12 +94,67 @@
* @param request the request to process
* @param callback the callback to provide the processed request, invoked from the main thread
*/
+ // TODO: Delete once SCREENSHOT_METADATA flag is launched
fun processAsync(request: ScreenshotRequest, callback: Consumer<ScreenshotRequest>) {
mainScope.launch {
val result = process(request)
callback.accept(result)
}
}
+
+ /**
+ * Inspects the incoming ScreenshotData, potentially modifying it based upon policy.
+ *
+ * @param screenshot the screenshot to process
+ */
+ suspend fun process(screenshot: ScreenshotData): ScreenshotData {
+ var result = screenshot
+
+ // Apply work profile screenshots policy:
+ //
+ // If the focused app belongs to a work profile, transforms a full screen
+ // (or partial) screenshot request to a task snapshot (provided image) screenshot.
+
+ // Whenever displayContentInfo is fetched, the topComponent is also populated
+ // regardless of the managed profile status.
+
+ if (screenshot.type != TAKE_SCREENSHOT_PROVIDED_IMAGE &&
+ flags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ ) {
+ val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
+ Log.d(TAG, "findPrimaryContent: $info")
+ result.taskId = info.taskId
+ result.topComponent = info.component
+ result.userHandle = info.user
+
+ if (policy.isManagedProfile(info.user.identifier)) {
+ val image = capture.captureTask(info.taskId)
+ ?: error("Task snapshot returned a null Bitmap!")
+
+ // Provide the task snapshot as the screenshot
+ result.type = TAKE_SCREENSHOT_PROVIDED_IMAGE
+ result.bitmap = image
+ result.screenBounds = info.bounds
+ }
+ }
+
+ return result
+ }
+
+ /**
+ * Note: This is for compatibility with existing Java. Prefer the suspending function when
+ * calling from a Coroutine context.
+ *
+ * @param screenshot the screenshot to process
+ * @param callback the callback to provide the processed screenshot, invoked from the main
+ * thread
+ */
+ fun processAsync(screenshot: ScreenshotData, callback: Consumer<ScreenshotData>) {
+ mainScope.launch {
+ val result = process(screenshot)
+ callback.accept(result)
+ }
+ }
}
private const val TAG = "RequestProcessor"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 6d87922..9f7d4f0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -17,7 +17,6 @@
package com.android.systemui.screenshot;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY;
@@ -44,6 +43,7 @@
import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
import android.app.ICompatCameraControlCallback;
import android.app.Notification;
+import android.app.assist.AssistContent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -101,6 +101,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.util.Assert;
import com.google.common.util.concurrent.ListenableFuture;
@@ -272,6 +273,7 @@
private final ScrollCaptureClient mScrollCaptureClient;
private final PhoneWindow mWindow;
private final DisplayManager mDisplayManager;
+ private final DisplayTracker mDisplayTracker;
private final ScrollCaptureController mScrollCaptureController;
private final LongScreenshotData mLongScreenshotHolder;
private final boolean mIsLowRamDevice;
@@ -281,6 +283,7 @@
private final ActionIntentExecutor mActionExecutor;
private final UserManager mUserManager;
private final WorkProfileMessageController mWorkProfileMessageController;
+ private final AssistContentRequester mAssistContentRequester;
private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
if (DEBUG_INPUT) {
@@ -328,7 +331,9 @@
ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
ActionIntentExecutor actionExecutor,
UserManager userManager,
- WorkProfileMessageController workProfileMessageController
+ WorkProfileMessageController workProfileMessageController,
+ AssistContentRequester assistContentRequester,
+ DisplayTracker displayTracker
) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
@@ -354,6 +359,7 @@
});
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
+ mDisplayTracker = displayTracker;
final Context displayContext = context.createDisplayContext(getDefaultDisplay());
mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
mWindowManager = mContext.getSystemService(WindowManager.class);
@@ -361,6 +367,7 @@
mActionExecutor = actionExecutor;
mUserManager = userManager;
mWorkProfileMessageController = workProfileMessageController;
+ mAssistContentRequester = assistContentRequester;
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
@@ -390,16 +397,143 @@
ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED);
}
+ void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
+ RequestCallback requestCallback) {
+ Assert.isMainThread();
+ mCurrentRequestCallback = requestCallback;
+ if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) {
+ Rect bounds = getFullScreenRect();
+ screenshot.setBitmap(
+ mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(), bounds));
+ screenshot.setScreenBounds(bounds);
+ }
+
+ if (screenshot.getBitmap() == null) {
+ Log.e(TAG, "handleScreenshot: Screenshot bitmap was null");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ if (mCurrentRequestCallback != null) {
+ mCurrentRequestCallback.reportError();
+ }
+ return;
+ }
+
+ if (!isUserSetupComplete(Process.myUserHandle())) {
+ Log.w(TAG, "User setup not complete, displaying toast only");
+ // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+ // and sharing shouldn't be exposed to the user.
+ saveScreenshotAndToast(screenshot.getUserHandle(), finisher);
+ return;
+ }
+
+ mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
+ ClipboardOverlayController.SELF_PERMISSION);
+
+ mScreenshotTakenInPortrait =
+ mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
+
+ String oldPackageName = mPackageName;
+ mPackageName = screenshot.getPackageNameString();
+
+ mScreenBitmap = screenshot.getBitmap();
+ // Optimizations
+ mScreenBitmap.setHasAlpha(false);
+ mScreenBitmap.prepareToDraw();
+
+ prepareViewForNewScreenshot(screenshot, oldPackageName);
+
+ saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher,
+ this::showUiOnActionsReady, this::showUiOnQuickShareActionReady);
+
+ // The window is focusable by default
+ setWindowFocusable(true);
+ mScreenshotView.requestFocus();
+
+ enqueueScrollCaptureRequest(screenshot.getUserHandle());
+
+ attachWindow();
+
+ boolean showFlash = true;
+ if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ if (screenshot.getScreenBounds() != null
+ && aspectRatiosMatch(screenshot.getBitmap(), screenshot.getInsets(),
+ screenshot.getScreenBounds())) {
+ showFlash = false;
+ } else {
+ showFlash = true;
+ screenshot.setInsets(Insets.NONE);
+ screenshot.setScreenBounds(new Rect(0, 0, screenshot.getBitmap().getWidth(),
+ screenshot.getBitmap().getHeight()));
+ }
+ }
+
+ prepareAnimation(screenshot.getScreenBounds(), showFlash);
+
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+ mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
+ mContext.getDrawable(R.drawable.overlay_badge_background),
+ screenshot.getUserHandle()));
+ }
+ mScreenshotView.setScreenshot(screenshot);
+
+ if (screenshot.getTaskId() >= 0) {
+ mAssistContentRequester.requestAssistContent(screenshot.getTaskId(),
+ new AssistContentRequester.Callback() {
+ @Override
+ public void onAssistContentAvailable(AssistContent assistContent) {
+ screenshot.setContextUrl(assistContent.getWebUri());
+ }
+ });
+ }
+
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
+ setContentView(mScreenshotView);
+ // ignore system bar insets for the purpose of window layout
+ mWindow.getDecorView().setOnApplyWindowInsetsListener(
+ (v, insets) -> WindowInsets.CONSUMED);
+ mScreenshotHandler.cancelTimeout(); // restarted after animation
+ }
+
+ void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
+ withWindowAttached(() -> {
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ && mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
+ mScreenshotView.announceForAccessibility(mContext.getResources().getString(
+ R.string.screenshot_saving_work_profile_title));
+ } else {
+ mScreenshotView.announceForAccessibility(
+ mContext.getResources().getString(R.string.screenshot_saving_title));
+ }
+ });
+
+ mScreenshotView.reset();
+
+ if (mScreenshotView.isAttachedToWindow()) {
+ // if we didn't already dismiss for another reason
+ if (!mScreenshotView.isDismissing()) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
+ oldPackageName);
+ }
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
+ + "(dismissing=" + mScreenshotView.isDismissing() + ")");
+ }
+ }
+
+ mScreenshotView.setPackageName(mPackageName);
+
+ mScreenshotView.updateOrientation(
+ mWindowManager.getCurrentWindowMetrics().getWindowInsets());
+ }
+
@MainThread
void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
RequestCallback requestCallback) {
Assert.isMainThread();
mCurrentRequestCallback = requestCallback;
- DisplayMetrics displayMetrics = new DisplayMetrics();
- getDefaultDisplay().getRealMetrics(displayMetrics);
- takeScreenshotInternal(
- topComponent, finisher,
- new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
+ takeScreenshotInternal(topComponent, finisher, getFullScreenRect());
}
@MainThread
@@ -537,6 +671,7 @@
setWindowFocusable(false);
}
}, mActionExecutor, mFlags);
+ mScreenshotView.setDefaultDisplay(mDisplayTracker.getDefaultDisplayId());
mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
@@ -570,7 +705,8 @@
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
- Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop);
+ Bitmap screenshot = mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(),
+ crop);
if (screenshot == null) {
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
@@ -641,6 +777,42 @@
setWindowFocusable(true);
mScreenshotView.requestFocus();
+ enqueueScrollCaptureRequest(owner);
+
+ attachWindow();
+ prepareAnimation(screenRect, showFlash);
+
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+ mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
+ mContext.getDrawable(R.drawable.overlay_badge_background), owner));
+ }
+ mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
+ setContentView(mScreenshotView);
+ // ignore system bar insets for the purpose of window layout
+ mWindow.getDecorView().setOnApplyWindowInsetsListener(
+ (v, insets) -> WindowInsets.CONSUMED);
+ mScreenshotHandler.cancelTimeout(); // restarted after animation
+ }
+
+ private void prepareAnimation(Rect screenRect, boolean showFlash) {
+ mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "onPreDraw: startAnimation");
+ }
+ mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
+ startAnimation(screenRect, showFlash);
+ return true;
+ }
+ });
+ }
+
+ private void enqueueScrollCaptureRequest(UserHandle owner) {
// Wait until this window is attached to request because it is
// the reference used to locate the target window (below).
withWindowAttached(() -> {
@@ -678,30 +850,6 @@
}
});
});
-
- attachWindow();
- mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- if (DEBUG_WINDOW) {
- Log.d(TAG, "onPreDraw: startAnimation");
- }
- mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
- startAnimation(screenRect, showFlash);
- return true;
- }
- });
- if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
- mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
- mContext.getDrawable(R.drawable.overlay_badge_background), owner));
- }
- mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
-
- // ignore system bar insets for the purpose of window layout
- mWindow.getDecorView().setOnApplyWindowInsetsListener(
- (v, insets) -> WindowInsets.CONSUMED);
- mScreenshotHandler.cancelTimeout(); // restarted after animation
}
private void requestScrollCapture(UserHandle owner) {
@@ -714,7 +862,7 @@
mLastScrollCaptureRequest.cancel(true);
}
final ListenableFuture<ScrollCaptureResponse> future =
- mScrollCaptureClient.request(DEFAULT_DISPLAY);
+ mScrollCaptureClient.request(mDisplayTracker.getDefaultDisplayId());
mLastScrollCaptureRequest = future;
mLastScrollCaptureRequest.addListener(() ->
onScrollCaptureResponseReady(future, owner), mMainExecutor);
@@ -745,7 +893,8 @@
mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
getDefaultDisplay().getRealMetrics(displayMetrics);
- Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY,
+ Bitmap newScreenshot = mImageCapture.captureDisplay(
+ mDisplayTracker.getDefaultDisplayId(),
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
@@ -809,7 +958,8 @@
SCREENSHOT_REMOTE_RUNNER, 0, 0);
try {
WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
+ .overridePendingAppTransitionRemote(runner,
+ mDisplayTracker.getDefaultDisplayId());
} catch (Exception e) {
Log.e(TAG, "Error overriding screenshot app transition", e);
}
@@ -1147,13 +1297,19 @@
}
private Display getDefaultDisplay() {
- return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ return mDisplayManager.getDisplay(mDisplayTracker.getDefaultDisplayId());
}
private boolean allowLongScreenshots() {
return !mIsLowRamDevice;
}
+ private Rect getFullScreenRect() {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getDefaultDisplay().getRealMetrics(displayMetrics);
+ return new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
+ }
+
/** Does the aspect ratio of the bitmap with insets removed match the bounds. */
private static boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets,
Rect screenBounds) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
new file mode 100644
index 0000000..c43e4b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
@@ -0,0 +1,46 @@
+package com.android.systemui.screenshot
+
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.Insets
+import android.graphics.Rect
+import android.net.Uri
+import android.os.UserHandle
+import android.view.WindowManager.ScreenshotSource
+import android.view.WindowManager.ScreenshotType
+import com.android.internal.util.ScreenshotRequest
+
+/** ScreenshotData represents the current state of a single screenshot being acquired. */
+data class ScreenshotData(
+ @ScreenshotType var type: Int,
+ @ScreenshotSource var source: Int,
+ /** UserHandle for the owner of the app being screenshotted, if known. */
+ var userHandle: UserHandle?,
+ /** ComponentName of the top-most app in the screenshot. */
+ var topComponent: ComponentName?,
+ var screenBounds: Rect?,
+ var taskId: Int,
+ var insets: Insets,
+ var bitmap: Bitmap?,
+ /** App-provided URL representing the content the user was looking at in the screenshot. */
+ var contextUrl: Uri? = null,
+) {
+ val packageNameString: String
+ get() = if (topComponent == null) "" else topComponent!!.packageName
+
+ companion object {
+ @JvmStatic
+ fun fromRequest(request: ScreenshotRequest): ScreenshotData {
+ return ScreenshotData(
+ request.type,
+ request.source,
+ if (request.userId >= 0) UserHandle.of(request.userId) else null,
+ request.topComponent,
+ request.boundsInScreen,
+ request.taskId,
+ request.insets,
+ request.bitmap,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
index 3a35286..21a7310 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
@@ -32,12 +32,12 @@
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
-import android.view.Display.DEFAULT_DISPLAY
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.infra.ServiceConnector
import com.android.systemui.SystemUIService
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
import java.util.Arrays
import javax.inject.Inject
@@ -52,6 +52,7 @@
private val userMgr: UserManager,
private val atmService: IActivityTaskManager,
@Background val bgDispatcher: CoroutineDispatcher,
+ private val displayTracker: DisplayTracker
) : ScreenshotPolicy {
private val proxyConnector: ServiceConnector<IScreenshotProxy> =
@@ -64,7 +65,7 @@
)
override fun getDefaultDisplayId(): Int {
- return DEFAULT_DISPLAY
+ return displayTracker.defaultDisplayId
}
override suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 7c013a8..9bd3d38 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -39,6 +39,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -136,6 +137,7 @@
private final AccessibilityManager mAccessibilityManager;
private final GestureDetector mSwipeDetector;
+ private int mDefaultDisplay = Display.DEFAULT_DISPLAY;
private int mNavMode;
private boolean mOrientationPortrait;
private boolean mDirectionLTR;
@@ -168,6 +170,8 @@
private final ArrayList<OverlayActionChip> mSmartChips = new ArrayList<>();
private PendingInteraction mPendingInteraction;
+ // Should only be set/used if the SCREENSHOT_METADATA flag is set.
+ private ScreenshotData mScreenshotData;
private final InteractionJankMonitor mInteractionJankMonitor;
private long mDefaultTimeoutOfTimeoutHandler;
@@ -330,7 +334,7 @@
private void startInputListening() {
stopInputListening();
- mInputMonitor = new InputMonitorCompat("Screenshot", Display.DEFAULT_DISPLAY);
+ mInputMonitor = new InputMonitorCompat("Screenshot", mDefaultDisplay);
mInputEventReceiver = mInputMonitor.getInputReceiver(
Looper.getMainLooper(), Choreographer.getInstance(), ev -> {
if (ev instanceof MotionEvent) {
@@ -477,10 +481,21 @@
mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
}
+ void setScreenshot(ScreenshotData screenshot) {
+ mScreenshotData = screenshot;
+ setScreenshot(screenshot.getBitmap(), screenshot.getInsets());
+ mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, screenshot.getBitmap(),
+ screenshot.getInsets()));
+ }
+
void setPackageName(String packageName) {
mPackageName = packageName;
}
+ void setDefaultDisplay(int displayId) {
+ mDefaultDisplay = displayId;
+ }
+
void updateInsets(WindowInsets insets) {
int orientation = mContext.getResources().getConfiguration().orientation;
mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
@@ -815,9 +830,17 @@
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED, 0, mPackageName);
if (mFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
prepareSharedTransition();
- mActionExecutor.launchIntentAsync(
- ActionIntentCreator.INSTANCE.createShareIntent(
- imageData.uri, imageData.subject),
+
+ Intent shareIntent;
+ if (mFlags.isEnabled(Flags.SCREENSHOT_METADATA) && mScreenshotData != null
+ && mScreenshotData.getContextUrl() != null) {
+ shareIntent = ActionIntentCreator.INSTANCE.createShareIntentWithExtraText(
+ imageData.uri, mScreenshotData.getContextUrl().toString());
+ } else {
+ shareIntent = ActionIntentCreator.INSTANCE.createShareIntentWithSubject(
+ imageData.uri, imageData.subject);
+ }
+ mActionExecutor.launchIntentAsync(shareIntent,
imageData.shareTransition.get().bundle,
imageData.owner.getIdentifier(), false);
} else {
@@ -1119,6 +1142,7 @@
mQuickShareChip = null;
setAlpha(1);
mScreenshotStatic.setAlpha(1);
+ mScreenshotData = null;
}
private void startSharedTransition(ActionTransition transition) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 7b271a8..4214c8f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -59,6 +59,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagListenable.FlagEvent;
+import com.android.systemui.flags.Flags;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -221,8 +222,33 @@
return;
}
- mProcessor.processAsync(request,
- (r) -> dispatchToController(r, onSaved, callback));
+ if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_METADATA)) {
+ Log.d(TAG, "Processing screenshot data");
+ ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
+ mProcessor.processAsync(screenshotData,
+ (data) -> dispatchToController(data, onSaved, callback));
+ } else {
+ mProcessor.processAsync(request,
+ (r) -> dispatchToController(r, onSaved, callback));
+ }
+ }
+
+ private void dispatchToController(ScreenshotData screenshot,
+ Consumer<Uri> uriConsumer, RequestCallback callback) {
+
+ mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshot.getSource()), 0,
+ screenshot.getPackageNameString());
+
+ if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+ && screenshot.getBitmap() == null) {
+ Log.e(TAG, "Got null bitmap from screenshot message");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ callback.reportError();
+ return;
+ }
+
+ mScreenshot.handleScreenshot(screenshot, uriConsumer, callback);
}
private void dispatchToController(ScreenshotRequest request,
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
new file mode 100644
index 0000000..bb7f721
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings
+
+import android.view.Display
+import java.util.concurrent.Executor
+
+/**
+ * Display tracker for SystemUI.
+ *
+ * This tracker provides async access to display information, as well as callbacks for display
+ * changes.
+ */
+interface DisplayTracker {
+
+ /** The id for the default display for the current SystemUI instance. */
+ val defaultDisplayId: Int
+
+ /** All displays that should be associated with the current SystemUI instance. */
+ val allDisplays: Array<Display>
+
+ /**
+ * Add a [Callback] to be notified of display changes, including additions, removals, and
+ * configuration changes, on a particular [Executor].
+ */
+ fun addDisplayChangeCallback(callback: Callback, executor: Executor)
+
+ /**
+ * Add a [Callback] to be notified of display brightness changes, on a particular [Executor].
+ * This callback will trigger Callback#onDisplayChanged for a display brightness change.
+ */
+ fun addBrightnessChangeCallback(callback: Callback, executor: Executor)
+
+ /** Remove a [Callback] previously added. */
+ fun removeCallback(callback: Callback)
+
+ /** Ćallback for notifying of changes. */
+ interface Callback {
+
+ /** Notifies that a display has been added. */
+ @JvmDefault fun onDisplayAdded(displayId: Int) {}
+
+ /** Notifies that a display has been removed. */
+ @JvmDefault fun onDisplayRemoved(displayId: Int) {}
+
+ /** Notifies a display has been changed */
+ @JvmDefault fun onDisplayChanged(displayId: Int) {}
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt
new file mode 100644
index 0000000..5169f88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings
+
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+import android.os.Handler
+import android.view.Display
+import androidx.annotation.GuardedBy
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Assert
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+
+class DisplayTrackerImpl
+internal constructor(
+ val displayManager: DisplayManager,
+ @Background val backgroundHandler: Handler
+) : DisplayTracker {
+ override val defaultDisplayId: Int = Display.DEFAULT_DISPLAY
+ override val allDisplays: Array<Display>
+ get() = displayManager.displays
+
+ @GuardedBy("displayCallbacks")
+ private val displayCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList()
+ @GuardedBy("brightnessCallbacks")
+ private val brightnessCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList()
+
+ @VisibleForTesting
+ val displayChangedListener: DisplayManager.DisplayListener =
+ object : DisplayManager.DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {
+ val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
+ onDisplayAdded(displayId, list)
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {
+ val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
+ onDisplayRemoved(displayId, list)
+ }
+
+ override fun onDisplayChanged(displayId: Int) {
+ val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
+ onDisplayChanged(displayId, list)
+ }
+ }
+
+ @VisibleForTesting
+ val displayBrightnessChangedListener: DisplayManager.DisplayListener =
+ object : DisplayManager.DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {}
+
+ override fun onDisplayRemoved(displayId: Int) {}
+
+ override fun onDisplayChanged(displayId: Int) {
+ val list = synchronized(brightnessCallbacks) { brightnessCallbacks.toList() }
+ onDisplayChanged(displayId, list)
+ }
+ }
+
+ override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) {
+ synchronized(displayCallbacks) {
+ if (displayCallbacks.isEmpty()) {
+ displayManager.registerDisplayListener(displayChangedListener, backgroundHandler)
+ }
+ displayCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor))
+ }
+ }
+
+ override fun addBrightnessChangeCallback(
+ callback: DisplayTracker.Callback,
+ executor: Executor
+ ) {
+ synchronized(brightnessCallbacks) {
+ if (brightnessCallbacks.isEmpty()) {
+ displayManager.registerDisplayListener(
+ displayBrightnessChangedListener,
+ backgroundHandler,
+ EVENT_FLAG_DISPLAY_BRIGHTNESS
+ )
+ }
+ brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor))
+ }
+ }
+
+ override fun removeCallback(callback: DisplayTracker.Callback) {
+ synchronized(displayCallbacks) {
+ val changed = displayCallbacks.removeIf { it.sameOrEmpty(callback) }
+ if (changed && displayCallbacks.isEmpty()) {
+ displayManager.unregisterDisplayListener(displayChangedListener)
+ }
+ }
+
+ synchronized(brightnessCallbacks) {
+ val changed = brightnessCallbacks.removeIf { it.sameOrEmpty(callback) }
+ if (changed && brightnessCallbacks.isEmpty()) {
+ displayManager.unregisterDisplayListener(displayBrightnessChangedListener)
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun onDisplayAdded(displayId: Int, list: List<DisplayTrackerDataItem>) {
+ Assert.isNotMainThread()
+
+ notifySubscribers({ onDisplayAdded(displayId) }, list)
+ }
+
+ @WorkerThread
+ private fun onDisplayRemoved(displayId: Int, list: List<DisplayTrackerDataItem>) {
+ Assert.isNotMainThread()
+
+ notifySubscribers({ onDisplayRemoved(displayId) }, list)
+ }
+
+ @WorkerThread
+ private fun onDisplayChanged(displayId: Int, list: List<DisplayTrackerDataItem>) {
+ Assert.isNotMainThread()
+
+ notifySubscribers({ onDisplayChanged(displayId) }, list)
+ }
+
+ private inline fun notifySubscribers(
+ crossinline action: DisplayTracker.Callback.() -> Unit,
+ list: List<DisplayTrackerDataItem>
+ ) {
+ list.forEach {
+ if (it.callback.get() != null) {
+ it.executor.execute { it.callback.get()?.action() }
+ }
+ }
+ }
+
+ private data class DisplayTrackerDataItem(
+ val callback: WeakReference<DisplayTracker.Callback>,
+ val executor: Executor
+ ) {
+ fun sameOrEmpty(other: DisplayTracker.Callback): Boolean {
+ return callback.get()?.equals(other) ?: true
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index b69786e..9594ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -27,10 +27,10 @@
import android.database.ContentObserver;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -49,6 +49,7 @@
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -73,19 +74,14 @@
private final ToggleSlider mControl;
private final DisplayManager mDisplayManager;
private final UserTracker mUserTracker;
+ private final DisplayTracker mDisplayTracker;
private final IVrManager mVrManager;
private final Executor mMainExecutor;
private final Handler mBackgroundHandler;
private final BrightnessObserver mBrightnessObserver;
- private final DisplayListener mDisplayListener = new DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {}
-
- @Override
- public void onDisplayRemoved(int displayId) {}
-
+ private final DisplayTracker.Callback mBrightnessListener = new DisplayTracker.Callback() {
@Override
public void onDisplayChanged(int displayId) {
mBackgroundHandler.post(mUpdateSliderRunnable);
@@ -133,14 +129,14 @@
cr.registerContentObserver(
BRIGHTNESS_MODE_URI,
false, this, UserHandle.USER_ALL);
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+ mDisplayTracker.addBrightnessChangeCallback(mBrightnessListener,
+ new HandlerExecutor(mHandler));
}
public void stopObserving() {
final ContentResolver cr = mContext.getContentResolver();
cr.unregisterContentObserver(this);
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ mDisplayTracker.removeCallback(mBrightnessListener);
}
}
@@ -282,6 +278,7 @@
Context context,
ToggleSlider control,
UserTracker userTracker,
+ DisplayTracker displayTracker,
@Main Executor mainExecutor,
@Background Handler bgHandler) {
mContext = context;
@@ -290,6 +287,7 @@
mMainExecutor = mainExecutor;
mBackgroundHandler = bgHandler;
mUserTracker = userTracker;
+ mDisplayTracker = displayTracker;
mBrightnessObserver = new BrightnessObserver(mHandler);
mDisplayId = mContext.getDisplayId();
@@ -424,6 +422,7 @@
public static class Factory {
private final Context mContext;
private final UserTracker mUserTracker;
+ private final DisplayTracker mDisplayTracker;
private final Executor mMainExecutor;
private final Handler mBackgroundHandler;
@@ -431,10 +430,12 @@
public Factory(
Context context,
UserTracker userTracker,
+ DisplayTracker displayTracker,
@Main Executor mainExecutor,
@Background Handler bgHandler) {
mContext = context;
mUserTracker = userTracker;
+ mDisplayTracker = displayTracker;
mMainExecutor = mainExecutor;
mBackgroundHandler = bgHandler;
}
@@ -445,6 +446,7 @@
mContext,
toggleSlider,
mUserTracker,
+ mDisplayTracker,
mMainExecutor,
mBackgroundHandler);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index e208be9..8879501 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -36,6 +36,7 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import java.util.List;
@@ -49,16 +50,19 @@
private BrightnessController mBrightnessController;
private final BrightnessSliderController.Factory mToggleSliderFactory;
private final UserTracker mUserTracker;
+ private final DisplayTracker mDisplayTracker;
private final Executor mMainExecutor;
private final Handler mBackgroundHandler;
@Inject
public BrightnessDialog(
UserTracker userTracker,
+ DisplayTracker displayTracker,
BrightnessSliderController.Factory factory,
@Main Executor mainExecutor,
@Background Handler bgHandler) {
mUserTracker = userTracker;
+ mDisplayTracker = displayTracker;
mToggleSliderFactory = factory;
mMainExecutor = mainExecutor;
mBackgroundHandler = bgHandler;
@@ -106,7 +110,7 @@
frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
mBrightnessController = new BrightnessController(
- this, controller, mUserTracker, mMainExecutor, mBackgroundHandler);
+ this, controller, mUserTracker, mDisplayTracker, mMainExecutor, mBackgroundHandler);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index 809fa29..e9a1dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.UserManager;
@@ -26,6 +27,8 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.settings.DisplayTrackerImpl;
import com.android.systemui.settings.UserContentResolverProvider;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserFileManager;
@@ -69,6 +72,15 @@
return tracker;
}
+ @SysUISingleton
+ @Provides
+ static DisplayTracker provideDisplayTracker(
+ DisplayManager displayManager,
+ @Background Handler handler
+ ) {
+ return new DisplayTrackerImpl(displayManager, handler);
+ }
+
@Binds
@IntoMap
@ClassKey(UserFileManagerImpl.class)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 8867637..197232e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import android.animation.Animator
import android.annotation.IdRes
import android.app.StatusBarManager
import android.content.res.Configuration
@@ -45,7 +46,6 @@
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
-import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -176,9 +176,13 @@
var shadeExpandedFraction = -1f
set(value) {
if (field != value) {
+ val oldAlpha = header.alpha
header.alpha = ShadeInterpolation.getContentAlpha(value)
field = value
- updateVisibility()
+ if ((oldAlpha == 0f && header.alpha > 0f) ||
+ (oldAlpha > 0f && header.alpha == 0f)) {
+ updateVisibility()
+ }
}
}
@@ -305,6 +309,8 @@
val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
v.pivotX = newPivot
v.pivotY = v.height.toFloat() / 2
+
+ qsCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
}
}
@@ -335,9 +341,28 @@
.setUpdateListener {
updateVisibility()
}
+ .setListener(endAnimationListener)
.start()
}
+ private val endAnimationListener = object : Animator.AnimatorListener {
+ override fun onAnimationCancel(animation: Animator?) {
+ clearListeners()
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ clearListeners()
+ }
+
+ override fun onAnimationRepeat(animation: Animator?) {}
+
+ override fun onAnimationStart(animation: Animator?) {}
+
+ private fun clearListeners() {
+ header.animate().setListener(null).setUpdateListener(null)
+ }
+ }
+
private fun loadConstraints() {
if (header is MotionLayout) {
// Use resources.getXml instead of passing the resource id due to bug b/205018300
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d11f9da..cf60c93 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -140,12 +140,16 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
@@ -204,7 +208,6 @@
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
@@ -243,6 +246,7 @@
import javax.inject.Inject;
import javax.inject.Provider;
+import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
@CentralSurfacesComponent.CentralSurfacesScope
@@ -698,6 +702,7 @@
private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final KeyguardInteractor mKeyguardInteractor;
private CoroutineDispatcher mMainDispatcher;
private boolean mIsOcclusionTransitionRunning = false;
private int mDreamingToLockscreenTransitionTranslationY;
@@ -827,7 +832,9 @@
LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
@Main CoroutineDispatcher mainDispatcher,
KeyguardTransitionInteractor keyguardTransitionInteractor,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ KeyguardLongPressViewModel keyguardLongPressViewModel,
+ KeyguardInteractor keyguardInteractor) {
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -848,6 +855,7 @@
mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mKeyguardInteractor = keyguardInteractor;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -1000,6 +1008,14 @@
updateUserSwitcherFlags();
mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
+ KeyguardLongPressViewBinder.bind(
+ mView.requireViewById(R.id.keyguard_long_press),
+ keyguardLongPressViewModel,
+ () -> {
+ onEmptySpaceClick();
+ return Unit.INSTANCE;
+ },
+ mFalsingManager);
onFinishInflate();
keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -2157,6 +2173,7 @@
}
ValueAnimator animator = createHeightAnimator(target, overshootAmount);
if (expand) {
+ maybeVibrateOnOpening(true /* openingWithTouch */);
if (expandBecauseOfFalsing && vel < 0) {
vel = 0;
}
@@ -2167,6 +2184,7 @@
animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
}
} else {
+ mHasVibratedOnOpen = false;
if (shouldUseDismissingAnimation()) {
if (vel == 0) {
animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
@@ -2516,7 +2534,7 @@
if (!mSplitShadeEnabled
&& computeQsExpansionFraction() <= 0.01 && getExpandedFraction() < 1.0) {
mShadeLog.logMotionEvent(event,
- "handleQsTouch: QQS touched while shade collapsing, QS tracking disabled");
+ "handleQsTouch: shade touched while collapsing, QS tracking disabled");
mQsTracking = false;
}
if (!mQsExpandImmediate && mQsTracking) {
@@ -3129,6 +3147,7 @@
mQsClipBottom,
radius,
qsVisible && !mSplitShadeEnabled);
+ mKeyguardInteractor.setQuickSettingsVisible(mQsVisible);
}
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusViewController.setClipBounds(clipStatusView ? mLastQsClipBounds : null);
@@ -3767,7 +3786,8 @@
// change due to "unlock hint animation." In this case, fading out the bottom area
// would also hide the message that says "swipe to unlock," we don't want to do that.
float expansionAlpha = MathUtils.map(
- isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
+ isUnlockHintRunning() ? 0 : KeyguardBouncerConstants.ALPHA_EXPANSION_THRESHOLD, 1f,
+ 0f, 1f,
getExpandedFraction());
float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
alpha *= mBottomAreaShadeAlpha;
@@ -6375,7 +6395,7 @@
mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
}
addMovement(event);
- if (!isFullyCollapsed()) {
+ if (!isFullyCollapsed() && !isOnKeyguard()) {
maybeVibrateOnOpening(true /* openingWithTouch */);
}
float h = y - mInitialExpandY;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index ab2e692..156e4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -563,7 +563,8 @@
mCurrentState.keyguardOccluded,
mCurrentState.bouncerShowing,
mCurrentState.dozing,
- mCurrentState.panelExpanded);
+ mCurrentState.panelExpanded,
+ mCurrentState.dreaming);
}
}
@@ -778,6 +779,12 @@
}
@Override
+ public void setDreaming(boolean dreaming) {
+ mCurrentState.dreaming = dreaming;
+ apply(mCurrentState);
+ }
+
+ @Override
public void setForcePluginOpen(boolean forceOpen, Object token) {
if (forceOpen) {
mCurrentState.forceOpenTokens.add(token);
@@ -904,5 +911,10 @@
public void onDozingChanged(boolean isDozing) {
setDozing(isDozing);
}
+
+ @Override
+ public void onDreamingChanged(boolean isDreaming) {
+ setDreaming(isDreaming);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index 736404aa..fed9b84 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -23,8 +23,8 @@
import com.android.systemui.statusbar.StatusBarState
/**
- * Represents state of shade window, used by [NotificationShadeWindowControllerImpl].
- * Contains nested class [Buffer] for pretty table logging in bug reports.
+ * Represents state of shade window, used by [NotificationShadeWindowControllerImpl]. Contains
+ * nested class [Buffer] for pretty table logging in bug reports.
*/
class NotificationShadeWindowState(
@JvmField var keyguardShowing: Boolean = false,
@@ -55,6 +55,7 @@
@JvmField var remoteInputActive: Boolean = false,
@JvmField var forcePluginOpen: Boolean = false,
@JvmField var dozing: Boolean = false,
+ @JvmField var dreaming: Boolean = false,
@JvmField var scrimsVisibility: Int = 0,
@JvmField var backgroundBlurRadius: Int = 0,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index b02a45a..393279b 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -18,7 +18,6 @@
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
-import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import dagger.Binds
import dagger.BindsOptionalOf
@@ -35,11 +34,6 @@
const val DREAM_SMARTSPACE_DATA_PLUGIN = "dreams_smartspace_data_plugin"
/**
- * The lockscreen smartspace target filter.
- */
- const val LOCKSCREEN_SMARTSPACE_TARGET_FILTER = "lockscreen_smartspace_target_filter"
-
- /**
* The dream smartspace target filter.
*/
const val DREAM_SMARTSPACE_TARGET_FILTER = "dream_smartspace_target_filter"
@@ -48,6 +42,11 @@
* The precondition for dream smartspace
*/
const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition"
+
+ /**
+ * The BcSmartspaceDataPlugin for the standalone weather.
+ */
+ const val WEATHER_SMARTSPACE_DATA_PLUGIN = "weather_smartspace_data_plugin"
}
@BindsOptionalOf
@@ -59,12 +58,6 @@
abstract fun optionalDreamsBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin?
@Binds
- @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
- abstract fun provideLockscreenSmartspaceTargetFilter(
- filter: LockscreenAndDreamTargetFilter?
- ): SmartspaceTargetFilter?
-
- @Binds
@Named(DREAM_SMARTSPACE_PRECONDITION)
abstract fun bindSmartspacePrecondition(
lockscreenPrecondition: LockscreenPrecondition?
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt b/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
index 1302ec9..88e8ad9 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
@@ -15,8 +15,6 @@
*/
package com.android.systemui.smartspace.preconditions
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.Execution
@@ -24,11 +22,9 @@
/**
* {@link LockscreenPrecondition} covers the conditions that must be met before Smartspace can be
- * used over lockscreen. These conditions include the device being provisioned with a setup user
- * and the Smartspace feature flag enabled.
+ * used over lockscreen. These conditions include the device being provisioned with a setup user.
*/
class LockscreenPrecondition @Inject constructor(
- private val featureFlags: FeatureFlags,
private val deviceProvisionedController: DeviceProvisionedController,
private val execution: Execution
) : SmartspacePrecondition {
@@ -90,6 +86,6 @@
override fun conditionsMet(): Boolean {
execution.assertIsMainThread()
- return featureFlags.isEnabled(Flags.SMARTSPACE) && deviceReady
+ return deviceReady
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 2f25244..3aaad87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -20,7 +20,6 @@
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
@@ -38,7 +37,6 @@
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
-import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.media.INearbyMediaDevicesProvider;
@@ -46,6 +44,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -53,7 +52,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.Pair;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
@@ -61,6 +59,7 @@
import android.view.WindowInsetsController.Behavior;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IAddTileResultCallback;
@@ -71,6 +70,7 @@
import com.android.internal.util.GcUtils;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.dump.DumpHandler;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -90,8 +90,7 @@
* are coalesced, note that they are all idempotent.
*/
public class CommandQueue extends IStatusBar.Stub implements
- CallbackController<Callbacks>,
- DisplayManager.DisplayListener {
+ CallbackController<Callbacks> {
private static final String TAG = CommandQueue.class.getSimpleName();
private static final int INDEX_MASK = 0xffff;
@@ -190,6 +189,7 @@
* event.
*/
private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
+ private final DisplayTracker mDisplayTracker;
private ProtoTracer mProtoTracer;
private final @Nullable CommandRegistry mRegistry;
private final @Nullable DumpHandler mDumpHandler;
@@ -352,7 +352,7 @@
}
/**
- * @see DisplayManager.DisplayListener#onDisplayRemoved(int)
+ * @see DisplayTracker.Callback#onDisplayRemoved(int)
*/
default void onDisplayRemoved(int displayId) {
}
@@ -501,46 +501,43 @@
default void showMediaOutputSwitcher(String packageName) {}
}
- public CommandQueue(Context context) {
- this(context, null, null, null);
+ @VisibleForTesting
+ public CommandQueue(Context context, DisplayTracker displayTracker) {
+ this(context, displayTracker, null, null, null);
}
public CommandQueue(
Context context,
+ DisplayTracker displayTracker,
ProtoTracer protoTracer,
CommandRegistry registry,
DumpHandler dumpHandler
) {
+ mDisplayTracker = displayTracker;
mProtoTracer = protoTracer;
mRegistry = registry;
mDumpHandler = dumpHandler;
- context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
+ mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() {
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mDisplayDisabled.remove(displayId);
+ }
+ // This callback is registered with {@link #mHandler} that already posts to run on
+ // main thread, so it is safe to dispatch directly.
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).onDisplayRemoved(displayId);
+ }
+ }
+ }, new HandlerExecutor(mHandler));
// We always have default display.
- setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
+ setDisabled(mDisplayTracker.getDefaultDisplayId(), DISABLE_NONE, DISABLE2_NONE);
}
- @Override
- public void onDisplayAdded(int displayId) { }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- synchronized (mLock) {
- mDisplayDisabled.remove(displayId);
- }
- // This callback is registered with {@link #mHandler} that already posts to run on main
- // thread, so it is safe to dispatch directly.
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).onDisplayRemoved(displayId);
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) { }
-
// TODO(b/118592525): add multi-display support if needed.
public boolean panelsEnabled() {
- final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
- final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
+ final int disabled1 = getDisabled1(mDisplayTracker.getDefaultDisplayId());
+ final int disabled2 = getDisabled2(mDisplayTracker.getDefaultDisplayId());
return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
&& (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0;
}
@@ -1271,8 +1268,7 @@
public void showMediaOutputSwitcher(String packageName) {
int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- Slog.e(TAG, "Call only allowed from system server.");
- return;
+ throw new SecurityException("Call only allowed from system server.");
}
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 63179da..5adb58b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -77,6 +77,12 @@
*/
public static void fadeOut(View view, float fadeOutAmount, boolean remap) {
view.animate().cancel();
+
+ // Don't fade out if already not visible.
+ if (view.getAlpha() == 0.0f) {
+ return;
+ }
+
if (fadeOutAmount == 1.0f && view.getVisibility() != View.GONE) {
view.setVisibility(View.INVISIBLE);
} else if (view.getVisibility() == View.INVISIBLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0f52133..1c4e319 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -43,6 +43,7 @@
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import static com.android.systemui.plugins.log.LogLevel.ERROR;
+import android.app.AlarmManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -99,6 +100,7 @@
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -128,10 +130,8 @@
private static final String TAG = "KeyguardIndication";
private static final boolean DEBUG_CHARGING_SPEED = false;
- private static final int MSG_HIDE_TRANSIENT = 1;
- private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
- private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3;
- private static final int MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON = 4;
+ private static final int MSG_SHOW_ACTION_TO_UNLOCK = 1;
+ private static final int MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON = 2;
private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
public static final long DEFAULT_HIDE_DELAY_MS =
3500 + KeyguardIndicationTextView.Y_IN_DURATION;
@@ -213,6 +213,11 @@
};
private boolean mFaceLockedOutThisAuthSession;
+ // Use AlarmTimeouts to guarantee that the events are handled even if scheduled and
+ // triggered while the device is asleep
+ private final AlarmTimeout mHideTransientMessageHandler;
+ private final AlarmTimeout mHideBiometricMessageHandler;
+
/**
* Creates a new KeyguardIndicationController and registers callbacks.
*/
@@ -239,7 +244,9 @@
AccessibilityManager accessibilityManager,
FaceHelpMessageDeferral faceHelpMessageDeferral,
KeyguardLogger keyguardLogger,
- AlternateBouncerInteractor alternateBouncerInteractor) {
+ AlternateBouncerInteractor alternateBouncerInteractor,
+ AlarmManager alarmManager
+ ) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -274,17 +281,26 @@
mHandler = new Handler(mainLooper) {
@Override
public void handleMessage(Message msg) {
- if (msg.what == MSG_HIDE_TRANSIENT) {
- hideTransientIndication();
- } else if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) {
+ if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) {
showActionToUnlock();
- } else if (msg.what == MSG_HIDE_BIOMETRIC_MESSAGE) {
- hideBiometricMessage();
} else if (msg.what == MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON) {
mBiometricErrorMessageToShowOnScreenOn = null;
}
}
};
+
+ mHideTransientMessageHandler = new AlarmTimeout(
+ alarmManager,
+ this::hideTransientIndication,
+ TAG,
+ mHandler
+ );
+ mHideBiometricMessageHandler = new AlarmTimeout(
+ alarmManager,
+ this::hideBiometricMessage,
+ TAG,
+ mHandler
+ );
}
/** Call this after construction to finish setting up the instance. */
@@ -336,6 +352,8 @@
*/
public void destroy() {
mHandler.removeCallbacksAndMessages(null);
+ mHideBiometricMessageHandler.cancel();
+ mHideTransientMessageHandler.cancel();
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
}
@@ -691,7 +709,7 @@
if (visible) {
// If this is called after an error message was already shown, we should not clear it.
// Otherwise the error message won't be shown
- if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
+ if (!mHideTransientMessageHandler.isScheduled()) {
hideTransientIndication();
}
updateDeviceEntryIndication(false);
@@ -739,16 +757,14 @@
* Hides transient indication in {@param delayMs}.
*/
public void hideTransientIndicationDelayed(long delayMs) {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
+ mHideTransientMessageHandler.schedule(delayMs, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED);
}
/**
* Hides biometric indication in {@param delayMs}.
*/
public void hideBiometricMessageDelayed(long delayMs) {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_HIDE_BIOMETRIC_MESSAGE), delayMs);
+ mHideBiometricMessageHandler.schedule(delayMs, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED);
}
/**
@@ -763,7 +779,6 @@
*/
private void showTransientIndication(CharSequence transientIndication) {
mTransientIndication = transientIndication;
- mHandler.removeMessages(MSG_HIDE_TRANSIENT);
hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS);
updateTransient();
@@ -789,7 +804,6 @@
mBiometricMessageFollowUp = biometricMessageFollowUp;
mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
- mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
hideBiometricMessageDelayed(
mBiometricMessageFollowUp != null
? IMPORTANT_MSG_MIN_DURATION * 2
@@ -803,7 +817,7 @@
if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
mBiometricMessage = null;
mBiometricMessageFollowUp = null;
- mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
+ mHideBiometricMessageHandler.cancel();
updateBiometricMessage();
}
}
@@ -814,7 +828,7 @@
public void hideTransientIndication() {
if (mTransientIndication != null) {
mTransientIndication = null;
- mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ mHideTransientMessageHandler.cancel();
updateTransient();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 0b1807d..2ca0b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -143,6 +143,9 @@
/** Sets the state of whether sysui is dozing or not. */
default void setDozing(boolean dozing) {}
+ /** Sets the state of whether sysui is dreaming or not. */
+ default void setDreaming(boolean dreaming) {}
+
/** Sets the state of whether plugin open is forced or not. */
default void setForcePluginOpen(boolean forcePluginOpen, Object token) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 58ce447..b9ac918 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -128,6 +128,11 @@
private boolean mIsDozing;
/**
+ * If the device is currently dreaming or not.
+ */
+ private boolean mIsDreaming;
+
+ /**
* If the status bar is currently expanded or not.
*/
private boolean mIsExpanded;
@@ -293,6 +298,29 @@
}
@Override
+ public boolean setIsDreaming(boolean isDreaming) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "setIsDreaming:" + isDreaming);
+ }
+ if (mIsDreaming == isDreaming) {
+ return false;
+ }
+
+ mIsDreaming = isDreaming;
+
+ synchronized (mListeners) {
+ String tag = getClass().getSimpleName() + "#setIsDreaming";
+ DejankUtils.startDetectingBlockingIpcs(tag);
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.mListener.onDreamingChanged(isDreaming);
+ }
+ DejankUtils.stopDetectingBlockingIpcs(tag);
+ }
+
+ return true;
+ }
+
+ @Override
public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) {
if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
if (animated && mDozeAmountTarget == dozeAmount) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 5a392a9..4043fce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -99,6 +99,13 @@
boolean setIsDozing(boolean isDozing);
/**
+ * Update the dreaming state from {@link CentralSurfaces}'s perspective
+ * @param isDreaming whether we are dreaming
+ * @return {@code true} if the state changed, else {@code false}
+ */
+ boolean setIsDreaming(boolean isDreaming);
+
+ /**
* Changes the current doze amount, also starts the
* {@link com.android.internal.jank.InteractionJankMonitor InteractionJankMonitor} as possible.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 9a65e34..d7568a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -25,17 +25,21 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.AnimationFeatureFlags;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.MediaArtworkProcessor;
@@ -181,11 +185,12 @@
@SysUISingleton
static CommandQueue provideCommandQueue(
Context context,
+ DisplayTracker displayTracker,
ProtoTracer protoTracer,
CommandRegistry registry,
DumpHandler dumpHandler
) {
- return new CommandQueue(context, protoTracer, registry, dumpHandler);
+ return new CommandQueue(context, displayTracker, protoTracer, registry, dumpHandler);
}
/**
@@ -281,7 +286,8 @@
static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
KeyguardStateController keyguardStateController,
Lazy<AlternateBouncerInteractor> alternateBouncerInteractor,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ AnimationFeatureFlags animationFeatureFlags) {
DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
@Override
public boolean isDreaming() {
@@ -303,6 +309,19 @@
return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint();
}
};
- return new DialogLaunchAnimator(callback, interactionJankMonitor);
+ return new DialogLaunchAnimator(callback, interactionJankMonitor, animationFeatureFlags);
+ }
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
+ static AnimationFeatureFlags provideAnimationFeatureFlags(FeatureFlags featureFlags) {
+ return new AnimationFeatureFlags() {
+ @Override
+ public boolean isPredictiveBackQsDialogAnim() {
+ return featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM);
+ }
+ };
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
index 3a4731a..92a8356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
@@ -20,9 +20,9 @@
import android.annotation.CallSuper
import android.os.Looper
import android.view.Choreographer
-import android.view.Display
import android.view.InputEvent
import android.view.MotionEvent
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.InputChannelCompat
import com.android.systemui.shared.system.InputMonitorCompat
@@ -38,7 +38,8 @@
* gesture is detected, they should call [onGestureDetected] (which will notify the callbacks).
*/
abstract class GenericGestureDetector(
- private val tag: String
+ private val tag: String,
+ private val displayTracker: DisplayTracker
) {
/**
* Active callbacks, each associated with a tag. Gestures will only be monitored if
@@ -86,7 +87,7 @@
internal open fun startGestureListening() {
stopGestureListening()
- inputMonitor = InputMonitorCompat(tag, Display.DEFAULT_DISPLAY).also {
+ inputMonitor = InputMonitorCompat(tag, displayTracker.defaultDisplayId).also {
inputReceiver = it.getInputReceiver(
Looper.getMainLooper(),
Choreographer.getInstance(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 5ab3d7c..693ae66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.view.MotionEvent
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.window.StatusBarWindowController
import javax.inject.Inject
@@ -28,9 +29,10 @@
@Inject
constructor(
context: Context,
+ displayTracker: DisplayTracker,
logger: SwipeUpGestureLogger,
private val statusBarWindowController: StatusBarWindowController,
-) : SwipeUpGestureHandler(context, logger, loggerTag = LOGGER_TAG) {
+) : SwipeUpGestureHandler(context, displayTracker, logger, loggerTag = LOGGER_TAG) {
override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean {
// Gesture starts just below the status bar
return ev.y >= statusBarWindowController.statusBarHeight &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
index 5ecc35c..6d60f4a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
@@ -24,6 +24,7 @@
import android.view.MotionEvent.ACTION_MOVE
import android.view.MotionEvent.ACTION_UP
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
/**
* A class to detect a generic "swipe up" gesture. To be notified when the swipe up gesture is
@@ -32,9 +33,10 @@
@SysUISingleton
abstract class SwipeUpGestureHandler(
context: Context,
+ displayTracker: DisplayTracker,
private val logger: SwipeUpGestureLogger,
private val loggerTag: String,
-) : GenericGestureDetector(SwipeUpGestureHandler::class.simpleName!!) {
+) : GenericGestureDetector(SwipeUpGestureHandler::class.simpleName!!, displayTracker) {
private var startY: Float = 0f
private var startTime: Long = 0L
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
index 7ffb07a..a901d597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
@@ -21,6 +21,7 @@
import android.view.InputEvent
import android.view.MotionEvent
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
import javax.inject.Inject
/**
@@ -29,8 +30,9 @@
*/
@SysUISingleton
class TapGestureDetector @Inject constructor(
- private val context: Context
-) : GenericGestureDetector(TapGestureDetector::class.simpleName!!) {
+ private val context: Context,
+ displayTracker: DisplayTracker
+) : GenericGestureDetector(TapGestureDetector::class.simpleName!!, displayTracker) {
private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 803c282..5b62d30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -52,6 +52,7 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.shared.regionsampling.UpdateColorCallback
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -60,6 +61,7 @@
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
+import javax.inject.Named
/** Controller for managing the smartspace view on the lockscreen */
@SysUISingleton
@@ -82,6 +84,8 @@
@Main private val uiExecutor: Executor,
@Background private val bgExecutor: Executor,
@Main private val handler: Handler,
+ @Named(WEATHER_SMARTSPACE_DATA_PLUGIN)
+ optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>,
optionalPlugin: Optional<BcSmartspaceDataPlugin>,
optionalConfigPlugin: Optional<BcSmartspaceConfigPlugin>,
) {
@@ -90,6 +94,7 @@
}
private var session: SmartspaceSession? = null
+ private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null)
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null)
@@ -100,7 +105,7 @@
private val regionSamplingEnabled =
featureFlags.isEnabled(Flags.REGION_SAMPLING)
-
+ private var isContentUpdatedOnce = false
private var showNotifications = false
private var showSensitiveContentForCurrentUser = false
private var showSensitiveContentForManagedUser = false
@@ -115,19 +120,6 @@
override fun onViewAttachedToWindow(v: View) {
smartspaceViews.add(v as SmartspaceView)
- if (regionSamplingEnabled) {
- var regionSampler = RegionSampler(
- v,
- uiExecutor,
- bgExecutor,
- regionSamplingEnabled,
- updateFun
- )
- initializeTextColors(regionSampler)
- regionSampler.startRegionSampler()
- regionSamplers.put(v, regionSampler)
- }
-
connectSession()
updateTextColorFromWallpaper()
@@ -137,12 +129,6 @@
override fun onViewDetachedFromWindow(v: View) {
smartspaceViews.remove(v as SmartspaceView)
- if (regionSamplingEnabled) {
- var regionSampler = regionSamplers.getValue(v)
- regionSampler.stopRegionSampler()
- regionSamplers.remove(v)
- }
-
if (smartspaceViews.isEmpty()) {
disconnect()
}
@@ -151,8 +137,30 @@
private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
execution.assertIsMainThread()
+
+ // The weather data plugin takes unfiltered targets and performs the filtering internally.
+ weatherPlugin?.onTargetsAvailable(targets)
+
val filteredTargets = targets.filter(::filterSmartspaceTarget)
plugin?.onTargetsAvailable(filteredTargets)
+ if (!isContentUpdatedOnce) {
+ for (v in smartspaceViews) {
+ if (regionSamplingEnabled) {
+ var regionSampler = RegionSampler(
+ v as View,
+ uiExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ updateFun
+ )
+ initializeTextColors(regionSampler)
+ regionSamplers[v] = regionSampler
+ regionSampler.startRegionSampler()
+ }
+ updateTextColorFromWallpaper()
+ }
+ isContentUpdatedOnce = true
+ }
}
private val userTrackerCallback = object : UserTracker.Callback {
@@ -208,7 +216,14 @@
fun isEnabled(): Boolean {
execution.assertIsMainThread()
- return featureFlags.isEnabled(Flags.SMARTSPACE) && plugin != null
+ return plugin != null
+ }
+
+ fun isDateWeatherDecoupled(): Boolean {
+ execution.assertIsMainThread()
+
+ return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) &&
+ weatherPlugin != null
}
private fun updateBypassEnabled() {
@@ -217,6 +232,25 @@
}
/**
+ * Constructs the weather view and connects it to the smartspace service.
+ */
+ fun buildAndConnectWeatherView(parent: ViewGroup): View? {
+ execution.assertIsMainThread()
+
+ if (!isEnabled()) {
+ throw RuntimeException("Cannot build view when not enabled")
+ }
+ if (!isDateWeatherDecoupled()) {
+ throw RuntimeException("Cannot build weather view when not decoupled")
+ }
+
+ val view = buildView(parent, weatherPlugin)
+ connectSession()
+
+ return view
+ }
+
+ /**
* Constructs the smartspace view and connects it to the smartspace service.
*/
fun buildAndConnectView(parent: ViewGroup): View? {
@@ -226,17 +260,17 @@
throw RuntimeException("Cannot build view when not enabled")
}
- val view = buildView(parent)
+ val view = buildView(parent, plugin, configPlugin)
connectSession()
return view
}
- fun requestSmartspaceUpdate() {
- session?.requestSmartspaceUpdate()
- }
-
- private fun buildView(parent: ViewGroup): View? {
+ private fun buildView(
+ parent: ViewGroup,
+ plugin: BcSmartspaceDataPlugin?,
+ configPlugin: BcSmartspaceConfigPlugin? = null
+ ): View? {
if (plugin == null) {
return null
}
@@ -244,7 +278,7 @@
val ssView = plugin.getView(parent)
ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
ssView.registerDataProvider(plugin)
- ssView.registerConfigProvider(configPlugin)
+ configPlugin?.let { ssView.registerConfigProvider(it) }
ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) {
@@ -275,7 +309,8 @@
}
private fun connectSession() {
- if (plugin == null || session != null || smartspaceViews.isEmpty()) {
+ if (weatherPlugin == null && plugin == null) return
+ if (session != null || smartspaceViews.isEmpty()) {
return
}
@@ -312,15 +347,21 @@
statusBarStateController.addCallback(statusBarStateListener)
bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
- plugin.registerSmartspaceEventNotifier {
- e -> session?.notifySmartspaceEvent(e)
- }
+ weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
+ plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
updateBypassEnabled()
reloadSmartspace()
}
/**
+ * Requests the smartspace session for an update.
+ */
+ fun requestSmartspaceUpdate() {
+ session?.requestSmartspaceUpdate()
+ }
+
+ /**
* Disconnects the smartspace view from the smartspace service and cleans up any resources.
*/
fun disconnect() {
@@ -343,9 +384,13 @@
bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener)
session = null
+ weatherPlugin?.registerSmartspaceEventNotifier(null)
+ weatherPlugin?.onTargetsAvailable(emptyList())
+
plugin?.registerSmartspaceEventNotifier(null)
plugin?.onTargetsAvailable(emptyList())
- Log.d(TAG, "Ending smartspace session for lockscreen")
+
+ Log.d(TAG, "Ended smartspace session for lockscreen")
}
fun addListener(listener: SmartspaceTargetListener) {
@@ -359,8 +404,11 @@
}
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
+ if (isDateWeatherDecoupled()) {
+ return t.featureType != SmartspaceTarget.FEATURE_WEATHER
+ }
if (!showNotifications) {
- return t.getFeatureType() == SmartspaceTarget.FEATURE_WEATHER
+ return t.featureType == SmartspaceTarget.FEATURE_WEATHER
}
return when (t.userHandle) {
userTracker.userHandle -> {
@@ -399,7 +447,8 @@
private fun updateTextColorFromWallpaper() {
val wallpaperManager = WallpaperManager.getInstance(context)
- if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists()) {
+ if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists() ||
+ regionSamplers.isEmpty()) {
val wallpaperTextColor =
Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 635ed7c..4856759 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -32,8 +32,6 @@
fun isDevLoggingEnabled(): Boolean =
featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
- fun isSmartspaceDedupingEnabled(): Boolean = featureFlags.isEnabled(Flags.SMARTSPACE)
-
fun fullScreenIntentRequiresKeyguard(): Boolean =
featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index aeae89c..7e53d54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.KeyguardBypassController.OnBypassStateChangedListener
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
@@ -38,7 +39,6 @@
import javax.inject.Inject
import kotlin.math.min
-
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
dumpManager: DumpManager,
@@ -68,6 +68,7 @@
private var mLinearDozeAmount: Float = 0.0f
private var mDozeAmount: Float = 0.0f
private var mDozeAmountSource: String = "init"
+ private var mNotifsHiddenByDozeAmountOverride: Boolean = false
private var mNotificationVisibleAmount = 0.0f
private var mNotificationsVisible = false
private var mNotificationsVisibleForExpansion = false
@@ -130,6 +131,7 @@
}
}
}
+
/**
* True if we can show pulsing heads up notifications
*/
@@ -149,10 +151,19 @@
return canShow
}
+ private val bypassStateChangedListener = object : OnBypassStateChangedListener {
+ override fun onBypassStateChanged(isEnabled: Boolean) {
+ // When the bypass state changes, we have to check whether we should re-show the
+ // notifications by clearing the doze amount override which hides them.
+ maybeClearDozeAmountOverrideHidingNotifs()
+ }
+ }
+
init {
dumpManager.registerDumpable(this)
mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
+ bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
addListener(object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
if (isFullyHidden && mNotificationsVisibleForExpansion) {
@@ -261,12 +272,18 @@
setDozeAmount(linear, eased, source = "StatusBar")
}
- fun setDozeAmount(linear: Float, eased: Float, source: String) {
+ fun setDozeAmount(
+ linear: Float,
+ eased: Float,
+ source: String,
+ hidesNotifsByOverride: Boolean = false
+ ) {
val changed = linear != mLinearDozeAmount
logger.logSetDozeAmount(linear, eased, source, statusBarStateController.state, changed)
mLinearDozeAmount = linear
mDozeAmount = eased
mDozeAmountSource = source
+ mNotifsHiddenByDozeAmountOverride = hidesNotifsByOverride
mStackScrollerController.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
@@ -295,6 +312,8 @@
return
}
+ maybeClearDozeAmountOverrideHidingNotifs()
+
if (bypassController.bypassEnabled &&
newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED &&
(!statusBarStateController.isDozing || shouldAnimateVisibility())) {
@@ -325,7 +344,8 @@
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
if (statusBarStateController.state == StatusBarState.KEYGUARD) {
- setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
+ setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)",
+ hidesNotifsByOverride = true)
} else {
setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
}
@@ -335,6 +355,37 @@
}
/**
+ * If the last [setDozeAmount] call was an override to hide notifications, then this call will
+ * check for the set of states that may have caused that override, and if none of them still
+ * apply, and the device is awake or not on the keyguard, then dozeAmount will be reset to 0.
+ * This fixes bugs where the bypass state changing could result in stale overrides, hiding
+ * notifications either on the inside screen or even after unlock.
+ */
+ private fun maybeClearDozeAmountOverrideHidingNotifs() {
+ if (mNotifsHiddenByDozeAmountOverride) {
+ val onKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD
+ val dozing = statusBarStateController.isDozing
+ val bypass = bypassController.bypassEnabled
+ val animating =
+ screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()
+ // Overrides are set by [overrideDozeAmountIfAnimatingScreenOff] and
+ // [overrideDozeAmountIfBypass] based on 'animating' and 'bypass' respectively, so only
+ // clear the override if both those conditions are cleared. But also require either
+ // !dozing or !onKeyguard because those conditions should indicate that we intend
+ // notifications to be visible, and thus it is safe to unhide them.
+ val willRemove = (!onKeyguard || !dozing) && !bypass && !animating
+ logger.logMaybeClearDozeAmountOverrideHidingNotifs(
+ willRemove = willRemove,
+ onKeyguard = onKeyguard, dozing = dozing,
+ bypass = bypass, animating = animating,
+ )
+ if (willRemove) {
+ setDozeAmount(0f, 0f, source = "Removed: $mDozeAmountSource")
+ }
+ }
+ }
+
+ /**
* If we're playing the screen off animation, force the notification doze amount to be 1f (fully
* dozing). This is needed so that the notifications aren't briefly visible as the screen turns
* off and dozeAmount goes from 1f to 0f.
@@ -344,7 +395,8 @@
*/
private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
- setDozeAmount(1f, 1f, source = "Override: animating screen off")
+ setDozeAmount(1f, 1f, source = "Override: animating screen off",
+ hidesNotifsByOverride = true)
return true
}
@@ -430,6 +482,7 @@
pw.println("mLinearDozeAmount: $mLinearDozeAmount")
pw.println("mDozeAmount: $mDozeAmount")
pw.println("mDozeAmountSource: $mDozeAmountSource")
+ pw.println("mNotifsHiddenByDozeAmountOverride: $mNotifsHiddenByDozeAmountOverride")
pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
pw.println("mNotificationsVisible: $mNotificationsVisible")
pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
index de18b0c..4464531 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
@@ -46,6 +46,25 @@
)
}
+ fun logMaybeClearDozeAmountOverrideHidingNotifs(
+ willRemove: Boolean,
+ onKeyguard: Boolean,
+ dozing: Boolean,
+ bypass: Boolean,
+ animating: Boolean,
+ ) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 =
+ "willRemove=$willRemove onKeyguard=$onKeyguard dozing=$dozing" +
+ " bypass=$bypass animating=$animating"
+ },
+ { "maybeClearDozeAmountOverrideHidingNotifs() $str1" }
+ )
+ }
+
fun logOnDozeAmountChanged(linear: Float, eased: Float) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 76252d0..e996b78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -114,10 +114,10 @@
.onStart { emit(Unit) }
// for each change, lookup the new value
.map {
- secureSettings.getBoolForUser(
+ secureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
UserHandle.USER_CURRENT,
- )
+ ) == 1
}
// perform lookups on the bg thread pool
.flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 1399385..03a3ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -88,9 +88,7 @@
mCoordinators.add(viewConfigCoordinator)
mCoordinators.add(visualStabilityCoordinator)
mCoordinators.add(sensitiveContentCoordinator)
- if (notifPipelineFlags.isSmartspaceDedupingEnabled()) {
- mCoordinators.add(smartspaceDedupingCoordinator)
- }
+ mCoordinators.add(smartspaceDedupingCoordinator)
mCoordinators.add(headsUpCoordinator)
mCoordinators.add(gutsCoordinator)
mCoordinators.add(preparationCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 808638a..8436ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -16,27 +16,15 @@
package com.android.systemui.statusbar.notification.dagger;
-import android.app.INotificationManager;
import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutManager;
-import android.os.Handler;
-import android.view.accessibility.AccessibilityManager;
-import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeEventsModule;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -50,7 +38,6 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
@@ -72,22 +59,17 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
-import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.wmshell.BubblesManager;
-import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Provider;
import dagger.Binds;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -109,48 +91,6 @@
@Binds
StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl);
- /** Provides an instance of {@link NotificationGutsManager} */
- @SysUISingleton
- @Provides
- static NotificationGutsManager provideNotificationGutsManager(
- Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
- @Main Handler mainHandler,
- @Background Handler bgHandler,
- AccessibilityManager accessibilityManager,
- HighPriorityProvider highPriorityProvider,
- INotificationManager notificationManager,
- PeopleSpaceWidgetManager peopleSpaceWidgetManager,
- LauncherApps launcherApps,
- ShortcutManager shortcutManager,
- ChannelEditorDialogController channelEditorDialogController,
- UserContextProvider contextTracker,
- AssistantFeedbackController assistantFeedbackController,
- Optional<BubblesManager> bubblesManagerOptional,
- UiEventLogger uiEventLogger,
- OnUserInteractionCallback onUserInteractionCallback,
- ShadeController shadeController) {
- return new NotificationGutsManager(
- context,
- centralSurfacesOptionalLazy,
- mainHandler,
- bgHandler,
- accessibilityManager,
- highPriorityProvider,
- notificationManager,
- peopleSpaceWidgetManager,
- launcherApps,
- shortcutManager,
- channelEditorDialogController,
- contextTracker,
- assistantFeedbackController,
- bubblesManagerOptional,
- uiEventLogger,
- onUserInteractionCallback,
- shadeController
- );
- }
-
/** Provides an instance of {@link NotifGutsViewManager} */
@Binds
NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
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 ea12b82..37ff11d 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
@@ -44,12 +44,10 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.notification.ConversationIconFactory;
-import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -65,28 +63,29 @@
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
-import java.io.PrintWriter;
import java.util.Optional;
+import javax.inject.Inject;
+
import dagger.Lazy;
/**
* 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.
*/
+@SysUISingleton
public class NotificationGutsManager implements NotifGutsViewManager {
private static final String TAG = "NotificationGutsManager";
// Must match constant in Settings. Used to highlight preferences when linking to Settings.
private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final MetricsLogger mMetricsLogger;
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final HighPriorityProvider mHighPriorityProvider;
@@ -94,12 +93,9 @@
private final OnUserInteractionCallback mOnUserInteractionCallback;
// Dependencies:
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- private final StatusBarStateController mStatusBarStateController =
- Dependency.get(StatusBarStateController.class);
- private final DeviceProvisionedController mDeviceProvisionedController =
- Dependency.get(DeviceProvisionedController.class);
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final StatusBarStateController mStatusBarStateController;
+ private final DeviceProvisionedController mDeviceProvisionedController;
private final AssistantFeedbackController mAssistantFeedbackController;
// which notification is currently being longpress-examined by the user
@@ -124,9 +120,7 @@
private final ShadeController mShadeController;
private NotifGutsViewListener mGutsListener;
- /**
- * Injected constructor. See {@link NotificationsModule}.
- */
+ @Inject
public NotificationGutsManager(Context context,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
@Main Handler mainHandler,
@@ -143,7 +137,11 @@
Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback,
- ShadeController shadeController) {
+ ShadeController shadeController,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ StatusBarStateController statusBarStateController,
+ DeviceProvisionedController deviceProvisionedController,
+ MetricsLogger metricsLogger) {
mContext = context;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mMainHandler = mainHandler;
@@ -161,6 +159,10 @@
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
mShadeController = shadeController;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mStatusBarStateController = statusBarStateController;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mMetricsLogger = metricsLogger;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -372,7 +374,8 @@
mDeviceProvisionedController.isDeviceProvisioned(),
row.getIsNonblockable(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index ea0060a..8a50f2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -204,10 +204,11 @@
boolean isDeviceProvisioned,
boolean isNonblockable,
boolean wasShownHighPriority,
- AssistantFeedbackController assistantFeedbackController)
+ AssistantFeedbackController assistantFeedbackController,
+ MetricsLogger metricsLogger)
throws RemoteException {
mINotificationManager = iNotificationManager;
- mMetricsLogger = Dependency.get(MetricsLogger.class);
+ mMetricsLogger = metricsLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 149ec54..8b1a02b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -279,7 +279,8 @@
public void onManagedProfileChanged() {
if (mManagedProfileController.hasActiveProfile()) {
if (mAutoTracker.isAdded(WORK)) return;
- mHost.addTile(WORK);
+ final int position = mAutoTracker.getRestoredTilePosition(WORK);
+ mHost.addTile(WORK, position);
mAutoTracker.setTileAdded(WORK);
} else {
if (!mAutoTracker.isAdded(WORK)) return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 194d1a8..62ddcfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1410,12 +1410,14 @@
// to dismiss the lock screen until entering the SIM PIN.
// - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the
// keyguard.
+ // - Shade is in QQS over keyguard - swiping up should take us back to keyguard
if (!isKeyguardShowing()
|| mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
|| isOccluded()
|| !mKeyguardStateController.canDismissLockScreen()
|| mKeyguardViewMediator.isAnySimPinSecure()
- || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) {
+ || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)
+ || mNotificationPanelViewController.getBarState() == StatusBarState.SHADE_LOCKED) {
return;
}
@@ -3771,6 +3773,9 @@
});
} else if (mDozing && !unlocking) {
mScrimController.transitionTo(ScrimState.AOD);
+ // This will cancel the keyguardFadingAway animation if it is running. We need to do
+ // this as otherwise it can remain pending and leave keyguard in a weird state.
+ mUnlockScrimCallback.onCancelled();
} else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
} else if (mKeyguardStateController.isShowing() && mKeyguardUpdateMonitor.isDreaming()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
deleted file mode 100644
index 6873de7..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ /dev/null
@@ -1,699 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.statusbar.phone;
-
-import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Handler;
-import android.os.Trace;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-
-import com.android.internal.policy.SystemBarUtils;
-import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardSecurityModel;
-import com.android.keyguard.KeyguardSecurityView;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.KeyguardResetCallback;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.ListenerSet;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * A class which manages the primary (pin/pattern/password) bouncer on the lockscreen.
- * @deprecated Use KeyguardBouncerRepository
- */
-@Deprecated
-public class KeyguardBouncer {
-
- private static final String TAG = "PrimaryKeyguardBouncer";
- static final long BOUNCER_FACE_DELAY = 1200;
- public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
- protected final Context mContext;
- protected final ViewMediatorCallback mCallback;
- protected final ViewGroup mContainer;
- private final FalsingCollector mFalsingCollector;
- private final DismissCallbackRegistry mDismissCallbackRegistry;
- private final Handler mHandler;
- private final List<PrimaryBouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>();
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final KeyguardStateController mKeyguardStateController;
- private final KeyguardSecurityModel mKeyguardSecurityModel;
- private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
- private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onStrongAuthStateChanged(int userId) {
- mBouncerPromptReason = mCallback.getBouncerPromptReason();
- }
-
- @Override
- public void onLockedOutStateChanged(BiometricSourceType type) {
- if (type == BiometricSourceType.FINGERPRINT) {
- mBouncerPromptReason = mCallback.getBouncerPromptReason();
- }
- }
-
- @Override
- public void onNonStrongBiometricAllowedChanged(int userId) {
- mBouncerPromptReason = mCallback.getBouncerPromptReason();
- }
- };
- private final Runnable mRemoveViewRunnable = this::removeView;
- private final KeyguardBypassController mKeyguardBypassController;
- private KeyguardHostViewController mKeyguardViewController;
- private final ListenerSet<KeyguardResetCallback> mResetCallbacks = new ListenerSet<>();
- private final Runnable mResetRunnable = ()-> {
- if (mKeyguardViewController != null) {
- mKeyguardViewController.resetSecurityContainer();
- for (KeyguardResetCallback callback : mResetCallbacks) {
- callback.onKeyguardReset();
- }
- }
- };
-
- private int mStatusBarHeight;
- private float mExpansion = EXPANSION_HIDDEN;
- private boolean mShowingSoon;
- private int mBouncerPromptReason;
- private boolean mIsAnimatingAway;
- private boolean mIsScrimmed;
- private boolean mInitialized;
-
- private KeyguardBouncer(Context context, ViewMediatorCallback callback,
- ViewGroup container,
- DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector,
- PrimaryBouncerExpansionCallback expansionCallback,
- KeyguardStateController keyguardStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController keyguardBypassController, @Main Handler handler,
- KeyguardSecurityModel keyguardSecurityModel,
- KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
- mContext = context;
- mCallback = callback;
- mContainer = container;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mFalsingCollector = falsingCollector;
- mDismissCallbackRegistry = dismissCallbackRegistry;
- mHandler = handler;
- mKeyguardStateController = keyguardStateController;
- mKeyguardSecurityModel = keyguardSecurityModel;
- mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
- mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
- mKeyguardBypassController = keyguardBypassController;
- mExpansionCallbacks.add(expansionCallback);
- }
-
- /**
- * Get the KeyguardBouncer expansion
- * @return 1=HIDDEN, 0=SHOWING, in between 0 and 1 means the bouncer is in transition.
- */
- public float getExpansion() {
- return mExpansion;
- }
-
- /**
- * Enable/disable only the back button
- */
- public void setBackButtonEnabled(boolean enabled) {
- int vis = mContainer.getSystemUiVisibility();
- if (enabled) {
- vis &= ~View.STATUS_BAR_DISABLE_BACK;
- } else {
- vis |= View.STATUS_BAR_DISABLE_BACK;
- }
- mContainer.setSystemUiVisibility(vis);
- }
-
- public void show(boolean resetSecuritySelection) {
- show(resetSecuritySelection, true /* scrimmed */);
- }
-
- /**
- * Shows the bouncer.
- *
- * @param resetSecuritySelection Cleans keyguard view
- * @param isScrimmed true when the bouncer show show scrimmed, false when the user will be
- * dragging it and translation should be deferred.
- */
- public void show(boolean resetSecuritySelection, boolean isScrimmed) {
- final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
- try {
- Trace.beginSection("KeyguardBouncer#show");
-
- ensureView();
- mIsScrimmed = isScrimmed;
-
- // On the keyguard, we want to show the bouncer when the user drags up, but it's
- // not correct to end the falsing session. We still need to verify if those touches
- // are valid.
- // Later, at the end of the animation, when the bouncer is at the top of the screen,
- // onFullyShown() will be called and FalsingManager will stop recording touches.
- if (isScrimmed) {
- setExpansion(EXPANSION_VISIBLE);
- }
-
- if (resetSecuritySelection) {
- // showPrimarySecurityScreen() updates the current security method. This is needed
- // in case we are already showing and the current security method changed.
- showPrimarySecurityScreen();
- }
-
- if (mContainer.getVisibility() == View.VISIBLE || mShowingSoon) {
- // Calls to reset must resume the ViewControllers when in fullscreen mode
- if (needsFullscreenBouncer()) {
- mKeyguardViewController.onResume();
- }
- return;
- }
-
- final int activeUserId = KeyguardUpdateMonitor.getCurrentUser();
- final boolean allowDismissKeyguard = activeUserId == keyguardUserId;
-
- // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern)
- // is set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
- if (allowDismissKeyguard && mKeyguardViewController.dismiss(activeUserId)) {
- return;
- }
-
- // This condition may indicate an error on Android, so log it.
- if (!allowDismissKeyguard) {
- Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != "
- + keyguardUserId);
- }
-
- mShowingSoon = true;
-
- // Split up the work over multiple frames.
- DejankUtils.removeCallbacks(mResetRunnable);
- if (mKeyguardStateController.isFaceAuthEnabled()
- && !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())
- && !needsFullscreenBouncer()
- && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- BiometricSourceType.FACE)
- && !mKeyguardBypassController.getBypassEnabled()) {
- mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
- } else {
- DejankUtils.postAfterTraversal(mShowRunnable);
- }
-
- mKeyguardStateController.notifyBouncerShowing(true /* showing */);
- dispatchStartingToShow();
- } finally {
- Trace.endSection();
- }
- }
-
- public boolean isScrimmed() {
- return mIsScrimmed;
- }
-
- /**
- * This method must be called at the end of the bouncer animation when
- * the translation is performed manually by the user, otherwise FalsingManager
- * will never be notified and its internal state will be out of sync.
- */
- private void onFullyShown() {
- mFalsingCollector.onBouncerShown();
- if (mKeyguardViewController == null) {
- Log.e(TAG, "onFullyShown when view was null");
- } else {
- mKeyguardViewController.onResume();
- mContainer.announceForAccessibility(
- mKeyguardViewController.getAccessibilityTitleForCurrentMode());
- }
- }
-
- /**
- * @see #onFullyShown()
- */
- private void onFullyHidden() {
-
- }
-
- private void setVisibility(@View.Visibility int visibility) {
- mContainer.setVisibility(visibility);
- if (mKeyguardViewController != null) {
- mKeyguardViewController.onBouncerVisibilityChanged(visibility);
- }
- dispatchVisibilityChanged();
- }
-
- private final Runnable mShowRunnable = new Runnable() {
- @Override
- public void run() {
- setVisibility(View.VISIBLE);
- showPromptReason(mBouncerPromptReason);
- final CharSequence customMessage = mCallback.consumeCustomMessage();
- if (customMessage != null) {
- mKeyguardViewController.showErrorMessage(customMessage);
- }
- mKeyguardViewController.appear(mStatusBarHeight);
- mShowingSoon = false;
- if (mExpansion == EXPANSION_VISIBLE) {
- mKeyguardViewController.onResume();
- mKeyguardViewController.resetSecurityContainer();
- showPromptReason(mBouncerPromptReason);
- }
- }
- };
-
- /**
- * Show a string explaining why the security view needs to be solved.
- *
- * @param reason a flag indicating which string should be shown, see
- * {@link KeyguardSecurityView#PROMPT_REASON_NONE}
- * and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
- */
- public void showPromptReason(int reason) {
- if (mKeyguardViewController != null) {
- mKeyguardViewController.showPromptReason(reason);
- } else {
- Log.w(TAG, "Trying to show prompt reason on empty bouncer");
- }
- }
-
- public void showMessage(String message, ColorStateList colorState) {
- if (mKeyguardViewController != null) {
- mKeyguardViewController.showMessage(message, colorState);
- } else {
- Log.w(TAG, "Trying to show message on empty bouncer");
- }
- }
-
- private void cancelShowRunnable() {
- DejankUtils.removeCallbacks(mShowRunnable);
- mHandler.removeCallbacks(mShowRunnable);
- mShowingSoon = false;
- }
-
- public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) {
- ensureView();
- setDismissAction(r, cancelAction);
- show(false /* resetSecuritySelection */);
- }
-
- /**
- * Set the actions to run when the keyguard is dismissed or when the dismiss is cancelled. Those
- * actions will still be run even if this bouncer is not shown, for instance when authenticating
- * with an alternate authenticator like the UDFPS.
- */
- public void setDismissAction(OnDismissAction r, Runnable cancelAction) {
- mKeyguardViewController.setOnDismissAction(r, cancelAction);
- }
-
- public void hide(boolean destroyView) {
- Trace.beginSection("KeyguardBouncer#hide");
- if (isShowing()) {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
- SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
- mDismissCallbackRegistry.notifyDismissCancelled();
- }
- mIsScrimmed = false;
- mFalsingCollector.onBouncerHidden();
- mKeyguardStateController.notifyBouncerShowing(false /* showing */);
- cancelShowRunnable();
- if (mKeyguardViewController != null) {
- mKeyguardViewController.cancelDismissAction();
- mKeyguardViewController.cleanUp();
- }
- mIsAnimatingAway = false;
- setVisibility(View.INVISIBLE);
- if (destroyView) {
-
- // We have a ViewFlipper that unregisters a broadcast when being detached, which may
- // be slow because of AM lock contention during unlocking. We can delay it a bit.
- mHandler.postDelayed(mRemoveViewRunnable, 50);
- }
- Trace.endSection();
- }
-
- /**
- * See {@link StatusBarKeyguardViewManager#startPreHideAnimation}.
- */
- public void startPreHideAnimation(Runnable runnable) {
- mIsAnimatingAway = true;
- if (mKeyguardViewController != null) {
- mKeyguardViewController.startDisappearAnimation(runnable);
- } else if (runnable != null) {
- runnable.run();
- }
- }
-
- /**
- * Reset the state of the view.
- */
- public void reset() {
- cancelShowRunnable();
- inflateView();
- mFalsingCollector.onBouncerHidden();
- }
-
- public void onScreenTurnedOff() {
- if (mKeyguardViewController != null && mContainer.getVisibility() == View.VISIBLE) {
- mKeyguardViewController.onPause();
- }
- }
-
- public boolean isShowing() {
- return (mShowingSoon || mContainer.getVisibility() == View.VISIBLE)
- && mExpansion == EXPANSION_VISIBLE && !isAnimatingAway();
- }
-
- /**
- * {@link #show(boolean)} was called but we're not showing yet, or being dragged.
- */
- public boolean inTransit() {
- return mShowingSoon || mExpansion != EXPANSION_HIDDEN && mExpansion != EXPANSION_VISIBLE;
- }
-
- /**
- * @return {@code true} when bouncer's pre-hide animation already started but isn't completely
- * hidden yet, {@code false} otherwise.
- */
- public boolean isAnimatingAway() {
- return mIsAnimatingAway;
- }
-
- public void prepare() {
- boolean wasInitialized = mInitialized;
- ensureView();
- if (wasInitialized) {
- showPrimarySecurityScreen();
- }
- mBouncerPromptReason = mCallback.getBouncerPromptReason();
- }
-
- private void showPrimarySecurityScreen() {
- mKeyguardViewController.showPrimarySecurityScreen();
- }
-
- /**
- * Current notification panel expansion
- * @param fraction 0 when notification panel is collapsed and 1 when expanded.
- * @see StatusBarKeyguardViewManager#onPanelExpansionChanged
- */
- public void setExpansion(float fraction) {
- float oldExpansion = mExpansion;
- boolean expansionChanged = mExpansion != fraction;
- mExpansion = fraction;
- if (mKeyguardViewController != null && !mIsAnimatingAway) {
- mKeyguardViewController.setExpansion(fraction);
- }
-
- if (fraction == EXPANSION_VISIBLE && oldExpansion != EXPANSION_VISIBLE) {
- onFullyShown();
- dispatchFullyShown();
- } else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) {
- DejankUtils.postAfterTraversal(mResetRunnable);
- /*
- * There are cases where #hide() was not invoked, such as when
- * NotificationPanelViewController controls the hide animation. Make sure the state gets
- * updated by calling #hide() directly.
- */
- hide(false /* destroyView */);
- dispatchFullyHidden();
- } else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) {
- dispatchStartingToHide();
- if (mKeyguardViewController != null) {
- mKeyguardViewController.onStartingToHide();
- }
- }
-
- if (expansionChanged) {
- dispatchExpansionChanged();
- }
- }
-
- public boolean willDismissWithAction() {
- return mKeyguardViewController != null && mKeyguardViewController.hasDismissActions();
- }
-
- public int getTop() {
- if (mKeyguardViewController == null) {
- return 0;
- }
-
- return mKeyguardViewController.getTop();
- }
-
- protected void ensureView() {
- // Removal of the view might be deferred to reduce unlock latency,
- // in this case we need to force the removal, otherwise we'll
- // end up in an unpredictable state.
- boolean forceRemoval = mHandler.hasCallbacks(mRemoveViewRunnable);
- if (!mInitialized || forceRemoval) {
- inflateView();
- }
- }
-
- protected void inflateView() {
- removeView();
- mHandler.removeCallbacks(mRemoveViewRunnable);
-
- KeyguardBouncerComponent component = mKeyguardBouncerComponentFactory.create(mContainer);
- mKeyguardViewController = component.getKeyguardHostViewController();
- mKeyguardViewController.init();
-
- mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
- setVisibility(View.INVISIBLE);
-
- final WindowInsets rootInsets = mContainer.getRootWindowInsets();
- if (rootInsets != null) {
- mContainer.dispatchApplyWindowInsets(rootInsets);
- }
- mInitialized = true;
- }
-
- protected void removeView() {
- mContainer.removeAllViews();
- mInitialized = false;
- }
-
- /**
- * @return True if and only if the security method should be shown before showing the
- * notifications on Keyguard, like SIM PIN/PUK.
- */
- public boolean needsFullscreenBouncer() {
- SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser());
- return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
- }
-
- /**
- * Like {@link #needsFullscreenBouncer}, but uses the currently visible security method, which
- * makes this method much faster.
- */
- public boolean isFullscreenBouncer() {
- if (mKeyguardViewController != null) {
- SecurityMode mode = mKeyguardViewController.getCurrentSecurityMode();
- return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
- }
- return false;
- }
-
- /**
- * WARNING: This method might cause Binder calls.
- */
- public boolean isSecure() {
- return mKeyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()) != SecurityMode.None;
- }
-
- public boolean shouldDismissOnMenuPressed() {
- return mKeyguardViewController.shouldEnableMenuKey();
- }
-
- public boolean interceptMediaKey(KeyEvent event) {
- ensureView();
- return mKeyguardViewController.interceptMediaKey(event);
- }
-
- /**
- * @return true if the pre IME back event should be handled
- */
- public boolean dispatchBackKeyEventPreIme() {
- ensureView();
- return mKeyguardViewController.dispatchBackKeyEventPreIme();
- }
-
- public void notifyKeyguardAuthenticated(boolean strongAuth) {
- ensureView();
- mKeyguardViewController.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
- }
-
- private void dispatchFullyShown() {
- for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
- callback.onFullyShown();
- }
- }
-
- private void dispatchStartingToHide() {
- for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
- callback.onStartingToHide();
- }
- }
-
- private void dispatchStartingToShow() {
- for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
- callback.onStartingToShow();
- }
- }
-
- private void dispatchFullyHidden() {
- for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
- callback.onFullyHidden();
- }
- }
-
- private void dispatchExpansionChanged() {
- for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
- callback.onExpansionChanged(mExpansion);
- }
- }
-
- private void dispatchVisibilityChanged() {
- for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
- callback.onVisibilityChanged(mContainer.getVisibility() == View.VISIBLE);
- }
- }
-
- /**
- * Apply keyguard configuration from the currently active resources. This can be called when the
- * device configuration changes, to re-apply some resources that are qualified on the device
- * configuration.
- */
- public void updateResources() {
- if (mKeyguardViewController != null) {
- mKeyguardViewController.updateResources();
- }
- }
-
- public void dump(PrintWriter pw) {
- pw.println("KeyguardBouncer");
- pw.println(" isShowing(): " + isShowing());
- pw.println(" mStatusBarHeight: " + mStatusBarHeight);
- pw.println(" mExpansion: " + mExpansion);
- pw.println(" mKeyguardViewController; " + mKeyguardViewController);
- pw.println(" mShowingSoon: " + mShowingSoon);
- pw.println(" mBouncerPromptReason: " + mBouncerPromptReason);
- pw.println(" mIsAnimatingAway: " + mIsAnimatingAway);
- pw.println(" mInitialized: " + mInitialized);
- }
-
- /** Update keyguard position based on a tapped X coordinate. */
- public void updateKeyguardPosition(float x) {
- if (mKeyguardViewController != null) {
- mKeyguardViewController.updateKeyguardPosition(x);
- }
- }
-
- public void addKeyguardResetCallback(KeyguardResetCallback callback) {
- mResetCallbacks.addIfAbsent(callback);
- }
-
- public void removeKeyguardResetCallback(KeyguardResetCallback callback) {
- mResetCallbacks.remove(callback);
- }
-
- /**
- * Adds a callback to listen to bouncer expansion updates.
- */
- public void addBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
- if (!mExpansionCallbacks.contains(callback)) {
- mExpansionCallbacks.add(callback);
- }
- }
-
- /**
- * Removes a previously added callback. If the callback was never added, this methood
- * does nothing.
- */
- public void removeBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
- mExpansionCallbacks.remove(callback);
- }
-
- /** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */
- public static class Factory {
- private final Context mContext;
- private final ViewMediatorCallback mCallback;
- private final DismissCallbackRegistry mDismissCallbackRegistry;
- private final FalsingCollector mFalsingCollector;
- private final KeyguardStateController mKeyguardStateController;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final KeyguardBypassController mKeyguardBypassController;
- private final Handler mHandler;
- private final KeyguardSecurityModel mKeyguardSecurityModel;
- private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
-
- @Inject
- public Factory(Context context, ViewMediatorCallback callback,
- DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector,
- KeyguardStateController keyguardStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController keyguardBypassController, @Main Handler handler,
- KeyguardSecurityModel keyguardSecurityModel,
- KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
- mContext = context;
- mCallback = callback;
- mDismissCallbackRegistry = dismissCallbackRegistry;
- mFalsingCollector = falsingCollector;
- mKeyguardStateController = keyguardStateController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mKeyguardBypassController = keyguardBypassController;
- mHandler = handler;
- mKeyguardSecurityModel = keyguardSecurityModel;
- mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
- }
-
- /**
- * Construct a KeyguardBouncer that will exist in the given container.
- */
- public KeyguardBouncer create(ViewGroup container,
- PrimaryBouncerExpansionCallback expansionCallback) {
- return new KeyguardBouncer(mContext, mCallback, container,
- mDismissCallbackRegistry, mFalsingCollector, expansionCallback,
- mKeyguardStateController, mKeyguardUpdateMonitor,
- mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
- mKeyguardBouncerComponentFactory);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 4d14542..fe2a913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -38,6 +37,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import java.io.PrintWriter;
@@ -94,7 +94,8 @@
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ DisplayTracker displayTracker) {
mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone);
mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone);
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
@@ -104,7 +105,7 @@
mNavigationMode = mode;
});
- if (ctx.getDisplayId() == DEFAULT_DISPLAY) {
+ if (ctx.getDisplayId() == displayTracker.getDefaultDisplayId()) {
dumpManager.registerDumpable(getClass().getSimpleName(), this);
}
}
@@ -317,24 +318,27 @@
private final BatteryController mBatteryController;
private final NavigationModeController mNavModeController;
private final DumpManager mDumpManager;
+ private final DisplayTracker mDisplayTracker;
@Inject
public Factory(
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ DisplayTracker displayTracker) {
mDarkIconDispatcher = darkIconDispatcher;
mBatteryController = batteryController;
mNavModeController = navModeController;
mDumpManager = dumpManager;
+ mDisplayTracker = displayTracker;
}
/** Create an {@link LightBarController} */
public LightBarController create(Context context) {
return new LightBarController(context, mDarkIconDispatcher, mBatteryController,
- mNavModeController, mDumpManager);
+ mNavModeController, mDumpManager, mDisplayTracker);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 1d7dfe1..0814ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -122,6 +122,10 @@
public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
// May be called on any thread - only use thread safe operations.
+ if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
+ return LoaderResult.success(null);
+ }
+
if (!mWallpaperManager.isWallpaperSupported()) {
// When wallpaper is not supported, show the system wallpaper
return LoaderResult.success(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 24ad55d..11863627 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -501,7 +501,7 @@
@VisibleForTesting
protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) {
if (mStatusBarPipelineFlags.useNewWifiIcon()) {
- throw new IllegalStateException("Attempting to add a mobile icon while the new "
+ throw new IllegalStateException("Attempting to add a wifi icon while the new "
+ "icons are enabled is not supported");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f7b8745..c2ca7c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -129,7 +129,6 @@
private final ConfigurationController mConfigurationController;
private final NavigationModeController mNavigationModeController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
- private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
private final DreamOverlayStateController mDreamOverlayStateController;
@Nullable
@@ -206,28 +205,28 @@
Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()");
}
onBackPressed();
- if (shouldPlayBackAnimation()) {
+ if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
mPrimaryBouncerView.getDelegate().getBackCallback().onBackInvoked();
}
}
@Override
public void onBackProgressed(BackEvent event) {
- if (shouldPlayBackAnimation()) {
+ if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
mPrimaryBouncerView.getDelegate().getBackCallback().onBackProgressed(event);
}
}
@Override
public void onBackCancelled() {
- if (shouldPlayBackAnimation()) {
+ if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
mPrimaryBouncerView.getDelegate().getBackCallback().onBackCancelled();
}
}
@Override
public void onBackStarted(BackEvent event) {
- if (shouldPlayBackAnimation()) {
+ if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
mPrimaryBouncerView.getDelegate().getBackCallback().onBackStarted(event);
}
}
@@ -256,7 +255,6 @@
private View mNotificationContainer;
- @Nullable protected KeyguardBouncer mPrimaryBouncer;
protected boolean mRemoteInputActive;
private boolean mGlobalActionsVisible = false;
private boolean mLastGlobalActionsVisible = false;
@@ -281,7 +279,6 @@
private boolean mLastScreenOffAnimationPlaying;
private float mQsExpansion;
final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
- private boolean mIsModernBouncerEnabled;
private boolean mIsModernAlternateBouncerEnabled;
private boolean mIsUnoccludeTransitionFlagEnabled;
private boolean mIsBackAnimationEnabled;
@@ -329,7 +326,6 @@
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
NotificationMediaManager notificationMediaManager,
- KeyguardBouncer.Factory keyguardBouncerFactory,
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
Lazy<ShadeController> shadeController,
@@ -352,7 +348,6 @@
mKeyguardUpdateManager = keyguardUpdateMonitor;
mStatusBarStateController = sysuiStatusBarStateController;
mDockManager = dockManager;
- mKeyguardBouncerFactory = keyguardBouncerFactory;
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
mShadeController = shadeController;
mLatencyTracker = latencyTracker;
@@ -362,7 +357,6 @@
mPrimaryBouncerView = primaryBouncerView;
mFoldAodAnimationController = sysUIUnfoldComponent
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
- mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER);
mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER);
mAlternateBouncerInteractor = alternateBouncerInteractor;
mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
@@ -381,11 +375,7 @@
mBiometricUnlockController = biometricUnlockController;
ViewGroup container = mCentralSurfaces.getBouncerContainer();
- if (mIsModernBouncerEnabled) {
- mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
- } else {
- mPrimaryBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
- }
+ mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
mNotificationPanelViewController = notificationPanelViewController;
if (shadeExpansionStateManager != null) {
shadeExpansionStateManager.addExpansionListener(this);
@@ -552,11 +542,7 @@
* show if any subsequent events are to be handled.
*/
if (beginShowingBouncer(event)) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
- } else {
- mPrimaryBouncerInteractor.show(/* isScrimmed= */false);
- }
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */false);
}
if (!primaryBouncerIsOrWillBeShowing()) {
@@ -564,17 +550,9 @@
}
if (mKeyguardStateController.isShowing()) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.setExpansion(fraction);
- } else {
- mPrimaryBouncerInteractor.setPanelExpansion(fraction);
- }
+ mPrimaryBouncerInteractor.setPanelExpansion(fraction);
} else {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.setExpansion(EXPANSION_HIDDEN);
- } else {
- mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN);
- }
+ mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN);
}
}
@@ -604,24 +582,17 @@
/**
* Shows the notification keyguard or the bouncer depending on
- * {@link KeyguardBouncer#needsFullscreenBouncer()}.
+ * {@link #needsFullscreenBouncer()}.
*/
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
mCentralSurfaces.hideKeyguard();
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.show(true /* resetSecuritySelection */);
- } else {
- mPrimaryBouncerInteractor.show(true);
- }
+ mPrimaryBouncerInteractor.show(true);
} else {
mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
hideBouncer(false /* destroyView */);
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.prepare();
- }
}
}
updateStates();
@@ -648,11 +619,7 @@
*/
@VisibleForTesting
void hideBouncer(boolean destroyView) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.hide(destroyView);
- } else {
- mPrimaryBouncerInteractor.hide();
- }
+ mPrimaryBouncerInteractor.hide();
if (mKeyguardStateController.isShowing()) {
// If we were showing the bouncer and then aborting, we need to also clear out any
// potential actions unless we actually unlocked.
@@ -671,11 +638,7 @@
hideAlternateBouncer(false);
if (mKeyguardStateController.isShowing() && !isBouncerShowing()) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.show(false /* resetSecuritySelection */, scrimmed);
- } else {
- mPrimaryBouncerInteractor.show(scrimmed);
- }
+ mPrimaryBouncerInteractor.show(scrimmed);
}
updateStates();
}
@@ -708,13 +671,8 @@
// instead of the bouncer.
if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
if (!afterKeyguardGone) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.setDismissAction(mAfterKeyguardGoneAction,
- mKeyguardGoneCancelAction);
- } else {
- mPrimaryBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
- mKeyguardGoneCancelAction);
- }
+ mPrimaryBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
+ mKeyguardGoneCancelAction);
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
}
@@ -726,22 +684,13 @@
if (afterKeyguardGone) {
// we'll handle the dismiss action after keyguard is gone, so just show the
// bouncer
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.show(false /* resetSecuritySelection */);
- } else {
- mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
- }
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
} else {
// after authentication success, run dismiss action with the option to defer
// hiding the keyguard based on the return value of the OnDismissAction
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
- mKeyguardGoneCancelAction);
- } else {
- mPrimaryBouncerInteractor.setDismissAction(
- mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
- mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
- }
+ mPrimaryBouncerInteractor.setDismissAction(
+ mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
// bouncer will handle the dismiss action, so we no longer need to track it here
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
@@ -841,11 +790,7 @@
@Override
public void onFinishedGoingToSleep() {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.onScreenTurnedOff();
- } else {
- mPrimaryBouncerInteractor.onScreenTurnedOff();
- }
+ mPrimaryBouncerInteractor.onScreenTurnedOff();
}
@Override
@@ -939,11 +884,7 @@
@Override
public void startPreHideAnimation(Runnable finishRunnable) {
if (primaryBouncerIsShowing()) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.startPreHideAnimation(finishRunnable);
- } else {
- mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
- }
+ mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
mNotificationPanelViewController.startBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
@@ -1051,17 +992,7 @@
}
public void onThemeChanged() {
- if (mIsModernBouncerEnabled) {
- updateResources();
- return;
- }
- boolean wasShowing = primaryBouncerIsShowing();
- boolean wasScrimmed = primaryBouncerIsScrimmed();
-
- hideBouncer(true /* destroyView */);
- mPrimaryBouncer.prepare();
-
- if (wasShowing) showPrimaryBouncer(wasScrimmed);
+ updateResources();
}
public void onKeyguardFadedAway() {
@@ -1105,10 +1036,6 @@
* WARNING: This method might cause Binder calls.
*/
public boolean isSecure() {
- if (mPrimaryBouncer != null) {
- return mPrimaryBouncer.isSecure();
- }
-
return mKeyguardSecurityModel.getSecurityMode(
KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
}
@@ -1163,10 +1090,8 @@
}
public boolean isFullscreenBouncer() {
- if (mPrimaryBouncerView.getDelegate() != null) {
- return mPrimaryBouncerView.getDelegate().isFullScreenBouncer();
- }
- return mPrimaryBouncer != null && mPrimaryBouncer.isFullscreenBouncer();
+ return mPrimaryBouncerView.getDelegate() != null
+ && mPrimaryBouncerView.getDelegate().isFullScreenBouncer();
}
/**
@@ -1222,17 +1147,9 @@
!= (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
if (primaryBouncerDismissible || !showing || remoteInputActive) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.setBackButtonEnabled(true);
- } else {
- mPrimaryBouncerInteractor.setBackButtonEnabled(true);
- }
+ mPrimaryBouncerInteractor.setBackButtonEnabled(true);
} else {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.setBackButtonEnabled(false);
- } else {
- mPrimaryBouncerInteractor.setBackButtonEnabled(false);
- }
+ mPrimaryBouncerInteractor.setBackButtonEnabled(false);
}
}
@@ -1326,27 +1243,21 @@
}
public boolean shouldDismissOnMenuPressed() {
- if (mPrimaryBouncerView.getDelegate() != null) {
- return mPrimaryBouncerView.getDelegate().shouldDismissOnMenuPressed();
- }
- return mPrimaryBouncer != null && mPrimaryBouncer.shouldDismissOnMenuPressed();
+ return mPrimaryBouncerView.getDelegate() != null
+ && mPrimaryBouncerView.getDelegate().shouldDismissOnMenuPressed();
}
public boolean interceptMediaKey(KeyEvent event) {
- if (mPrimaryBouncerView.getDelegate() != null) {
- return mPrimaryBouncerView.getDelegate().interceptMediaKey(event);
- }
- return mPrimaryBouncer != null && mPrimaryBouncer.interceptMediaKey(event);
+ return mPrimaryBouncerView.getDelegate() != null
+ && mPrimaryBouncerView.getDelegate().interceptMediaKey(event);
}
/**
* @return true if the pre IME back event should be handled
*/
public boolean dispatchBackKeyEventPreIme() {
- if (mPrimaryBouncerView.getDelegate() != null) {
- return mPrimaryBouncerView.getDelegate().dispatchBackKeyEventPreIme();
- }
- return mPrimaryBouncer != null && mPrimaryBouncer.dispatchBackKeyEventPreIme();
+ return mPrimaryBouncerView.getDelegate() != null
+ && mPrimaryBouncerView.getDelegate().dispatchBackKeyEventPreIme();
}
public void readyForKeyguardDone() {
@@ -1392,11 +1303,7 @@
* fingerprint.
*/
public void notifyKeyguardAuthenticated(boolean strongAuth) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.notifyKeyguardAuthenticated(strongAuth);
- } else {
- mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
- }
+ mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
if (mAlternateBouncerInteractor.isVisibleState()) {
hideAlternateBouncer(false);
@@ -1411,11 +1318,7 @@
mKeyguardMessageAreaController.setMessage(message);
}
} else {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.showMessage(message, colorState);
- } else {
- mPrimaryBouncerInteractor.showMessage(message, colorState);
- }
+ mPrimaryBouncerInteractor.showMessage(message, colorState);
}
}
@@ -1471,11 +1374,7 @@
* configuration.
*/
public void updateResources() {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.updateResources();
- } else {
- mPrimaryBouncerInteractor.updateResources();
- }
+ mPrimaryBouncerInteractor.updateResources();
}
public void dump(PrintWriter pw) {
@@ -1493,11 +1392,6 @@
pw.println(" " + callback);
}
- if (mPrimaryBouncer != null) {
- pw.println("PrimaryBouncer:");
- mPrimaryBouncer.dump(pw);
- }
-
if (mOccludingAppBiometricUI != null) {
pw.println("mOccludingAppBiometricUI:");
mOccludingAppBiometricUI.dump(pw);
@@ -1547,11 +1441,6 @@
}
}
- @Nullable
- public KeyguardBouncer getPrimaryBouncer() {
- return mPrimaryBouncer;
- }
-
/**
* For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently
* showing.
@@ -1570,11 +1459,7 @@
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
- if (mPrimaryBouncer != null) {
- mPrimaryBouncer.updateKeyguardPosition(x);
- } else {
- mPrimaryBouncerInteractor.setKeyguardPosition(x);
- }
+ mPrimaryBouncerInteractor.setKeyguardPosition(x);
}
private static class DismissWithActionRequest {
@@ -1614,56 +1499,35 @@
* Returns if bouncer expansion is between 0 and 1 non-inclusive.
*/
public boolean isPrimaryBouncerInTransit() {
- if (mPrimaryBouncer != null) {
- return mPrimaryBouncer.inTransit();
- } else {
- return mPrimaryBouncerInteractor.isInTransit();
- }
+ return mPrimaryBouncerInteractor.isInTransit();
}
/**
* Returns if bouncer is showing
*/
public boolean primaryBouncerIsShowing() {
- if (mPrimaryBouncer != null) {
- return mPrimaryBouncer.isShowing();
- } else {
- return mPrimaryBouncerInteractor.isFullyShowing();
- }
+ return mPrimaryBouncerInteractor.isFullyShowing();
}
/**
* Returns if bouncer is scrimmed
*/
public boolean primaryBouncerIsScrimmed() {
- if (mPrimaryBouncer != null) {
- return mPrimaryBouncer.isScrimmed();
- } else {
- return mPrimaryBouncerInteractor.isScrimmed();
- }
+ return mPrimaryBouncerInteractor.isScrimmed();
}
/**
* Returns if bouncer is animating away
*/
public boolean bouncerIsAnimatingAway() {
- if (mPrimaryBouncer != null) {
- return mPrimaryBouncer.isAnimatingAway();
- } else {
- return mPrimaryBouncerInteractor.isAnimatingAway();
- }
-
+ return mPrimaryBouncerInteractor.isAnimatingAway();
}
/**
* Returns if bouncer will dismiss with action
*/
public boolean primaryBouncerWillDismissWithAction() {
- if (mPrimaryBouncer != null) {
- return mPrimaryBouncer.willDismissWithAction();
- } else {
- return mPrimaryBouncerInteractor.willDismissWithAction();
- }
+ return mPrimaryBouncerInteractor.willDismissWithAction();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
index ae48c2d3..50cce45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -17,5 +17,5 @@
public interface StatusBarWindowCallback {
void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing,
- boolean isDozing, boolean panelExpanded);
+ boolean isDozing, boolean panelExpanded, boolean isDreaming);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 510482d..39ad31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -53,7 +53,7 @@
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
-import com.android.systemui.util.kotlin.pairwiseBy
+import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -66,10 +66,10 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -287,22 +287,13 @@
*/
@SuppressLint("MissingPermission")
override val activeSubChangedInGroupEvent =
- flow {
- activeMobileDataSubscriptionId.pairwiseBy { prevVal: Int, newVal: Int ->
- if (!defaultMobileNetworkConnectivity.value.isValidated) {
- return@pairwiseBy
- }
- val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)
- val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)
+ activeMobileDataSubscriptionId
+ .pairwise()
+ .mapNotNull { (prevVal: Int, newVal: Int) ->
+ val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)?.groupUuid
+ val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)?.groupUuid
- if (prevSub == null || nextSub == null) {
- return@pairwiseBy
- }
-
- if (prevSub.groupUuid != null && prevSub.groupUuid == nextSub.groupUuid) {
- emit(Unit)
- }
- }
+ if (prevSub != null && prevSub == nextSub) Unit else null
}
.flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
index d3ff357..491f3a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
@@ -97,15 +97,20 @@
)
}
- fun logOnCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
+ fun logOnCapabilitiesChanged(
+ network: Network,
+ networkCapabilities: NetworkCapabilities,
+ isDefaultNetworkCallback: Boolean,
+ ) {
buffer.log(
SB_LOGGING_TAG,
LogLevel.INFO,
{
+ bool1 = isDefaultNetworkCallback
int1 = network.getNetId()
str1 = networkCapabilities.toString()
},
- { "onCapabilitiesChanged: net=$int1 capabilities=$str1" }
+ { "onCapabilitiesChanged[default=$bool1]: net=$int1 capabilities=$str1" }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index d26499c..8669047 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -114,13 +114,17 @@
network: Network,
networkCapabilities: NetworkCapabilities
) {
+ logger.logOnCapabilitiesChanged(
+ network,
+ networkCapabilities,
+ isDefaultNetworkCallback = true,
+ )
+
// This method will always be called immediately after the network
// becomes the default, in addition to any time the capabilities change
// while the network is the default.
- // If this network contains valid wifi info, then wifi is the default
- // network.
- val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
- trySend(wifiInfo != null)
+ // If this network is a wifi network, then wifi is the default network.
+ trySend(isWifiNetwork(networkCapabilities))
}
override fun onLost(network: Network) {
@@ -152,7 +156,11 @@
network: Network,
networkCapabilities: NetworkCapabilities
) {
- logger.logOnCapabilitiesChanged(network, networkCapabilities)
+ logger.logOnCapabilitiesChanged(
+ network,
+ networkCapabilities,
+ isDefaultNetworkCallback = false,
+ )
wifiNetworkChangeEvents.tryEmit(Unit)
@@ -253,16 +261,30 @@
networkCapabilities: NetworkCapabilities
): WifiInfo? {
return when {
- networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
- networkCapabilities.transportInfo as WifiInfo?
networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
// Sometimes, cellular networks can act as wifi networks (known as VCN --
// virtual carrier network). So, see if this cellular network has wifi info.
Utils.tryGetWifiInfoForVcn(networkCapabilities)
+ networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
+ if (networkCapabilities.transportInfo is WifiInfo) {
+ networkCapabilities.transportInfo as WifiInfo
+ } else {
+ null
+ }
else -> null
}
}
+ /** True if these capabilities represent a wifi network. */
+ private fun isWifiNetwork(networkCapabilities: NetworkCapabilities): Boolean {
+ return when {
+ networkCapabilities.hasTransport(TRANSPORT_WIFI) -> true
+ networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
+ Utils.tryGetWifiInfoForVcn(networkCapabilities) != null
+ else -> false
+ }
+ }
+
private fun createWifiNetworkModel(
wifiInfo: WifiInfo,
network: Network,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index 9946b4b..5dcafb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.policy;
import android.annotation.WorkerThread;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
@@ -255,7 +254,6 @@
setTorchMode(enabled);
mSecureSettings.putInt(Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
mSecureSettings.putInt(Secure.FLASHLIGHT_ENABLED, enabled ? 1 : 0);
- mBroadcastSender.sendBroadcast(new Intent(ACTION_FLASHLIGHT_CHANGED));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index f8c17e8..4866f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -303,7 +303,8 @@
mEntry.mRemoteEditImeVisible = editTextRootWindowInsets != null
&& editTextRootWindowInsets.isVisible(WindowInsets.Type.ime());
if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
- mController.removeRemoteInput(mEntry, mToken);
+ // Pass null to ensure all inputs are cleared for this entry b/227115380
+ mController.removeRemoteInput(mEntry, null);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
index 5a8850a..dde2a80 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
@@ -39,6 +39,17 @@
private val featureFlags: FeatureFlags,
) : CoreStartable, StylusManager.StylusCallback, StylusManager.StylusBatteryCallback {
+ override fun onStylusAdded(deviceId: Int) {
+ // On some devices, the addition of a new internal stylus indicates the use of a
+ // USI stylus with a different vendor/product ID. We would therefore like to reset
+ // the battery notification suppression, in case the user has dismissed a low battery
+ // notification of the previous stylus.
+ val device = inputManager.getInputDevice(deviceId) ?: return
+ if (!device.isExternal) {
+ stylusUsiPowerUi.updateSuppression(false)
+ }
+ }
+
override fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {
stylusUsiPowerUi.refresh()
}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index 8d5e01c..9050dad 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -29,13 +29,14 @@
import android.os.Handler
import android.os.UserHandle
import android.util.Log
-import android.view.InputDevice
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.hardware.hasInputDevice
+import com.android.systemui.shared.hardware.isAnyStylusSource
import com.android.systemui.util.NotificationChannels
import java.text.NumberFormat
import javax.inject.Inject
@@ -150,10 +151,7 @@
}
private fun hasConnectedBluetoothStylus(): Boolean {
- // TODO(b/257936830): get bt address once input api available
- return inputManager.inputDeviceIds.any { deviceId ->
- inputManager.getInputDevice(deviceId).supportsSource(InputDevice.SOURCE_STYLUS)
- }
+ return inputManager.hasInputDevice { it.isAnyStylusSource && it.bluetoothAddress != null }
}
private fun getPendingBroadcast(action: String): PendingIntent? {
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index df8d161..1065d33 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -327,7 +327,7 @@
// appropriately.
activeViews.remove(displayInfo)
listeners.forEach {
- it.onInfoPermanentlyRemoved(id)
+ it.onInfoPermanentlyRemoved(id, removalReason)
}
// No need to time the view out since it's already gone
@@ -393,7 +393,7 @@
activeViews.remove(it)
logger.logViewExpiration(it.info)
listeners.forEach { listener ->
- listener.onInfoPermanentlyRemoved(it.info.id)
+ listener.onInfoPermanentlyRemoved(it.info.id, REMOVAL_REASON_TIME_EXPIRED)
}
}
}
@@ -457,7 +457,7 @@
* Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
* displayed again (unless another call to [updateView] is made).
*/
- fun onInfoPermanentlyRemoved(id: String)
+ fun onInfoPermanentlyRemoved(id: String, reason: String)
}
/** A container for all the display-related state objects. */
@@ -494,6 +494,7 @@
}
private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
+private const val REMOVAL_REASON_TIME_EXPIRED = "TIMEOUT_EXPIRED_BEFORE_REDISPLAY"
private const val MIN_REQUIRED_TIME_FOR_REDISPLAY = 1000
private data class IconInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 9e0bbb7..46f13cc 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -206,31 +206,44 @@
}
override fun animateViewIn(view: ViewGroup) {
- ViewHierarchyAnimator.animateAddition(
- view.getInnerView(),
- ViewHierarchyAnimator.Hotspot.TOP,
- Interpolators.EMPHASIZED_DECELERATE,
- duration = ANIMATION_IN_DURATION,
- includeMargins = true,
- includeFadeIn = true,
- // We can only request focus once the animation finishes.
- onAnimationEnd = {
- maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
- },
- )
+ val onAnimationEnd = Runnable {
+ maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
+ }
+ val added =
+ ViewHierarchyAnimator.animateAddition(
+ view.getInnerView(),
+ ViewHierarchyAnimator.Hotspot.TOP,
+ Interpolators.EMPHASIZED_DECELERATE,
+ duration = ANIMATION_IN_DURATION,
+ includeMargins = true,
+ includeFadeIn = true,
+ // We can only request focus once the animation finishes.
+ onAnimationEnd = onAnimationEnd,
+ )
+ // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
+ // run it immediately.
+ if (!added) {
+ onAnimationEnd.run()
+ }
}
override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
val innerView = view.getInnerView()
innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
- ViewHierarchyAnimator.animateRemoval(
- innerView,
- ViewHierarchyAnimator.Hotspot.TOP,
- Interpolators.EMPHASIZED_ACCELERATE,
- ANIMATION_OUT_DURATION,
- includeMargins = true,
- onAnimationEnd,
- )
+ val removed =
+ ViewHierarchyAnimator.animateRemoval(
+ innerView,
+ ViewHierarchyAnimator.Hotspot.TOP,
+ Interpolators.EMPHASIZED_ACCELERATE,
+ ANIMATION_OUT_DURATION,
+ includeMargins = true,
+ onAnimationEnd,
+ )
+ // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
+ // run it immediately.
+ if (!removed) {
+ onAnimationEnd.run()
+ }
updateGestureListening()
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
index 6e3cb48..9dbc4b3 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.view.MotionEvent
import android.view.View
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.gesture.SwipeUpGestureHandler
import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
import com.android.systemui.util.boundsOnScreen
@@ -31,8 +32,9 @@
*/
class SwipeChipbarAwayGestureHandler(
context: Context,
+ displayTracker: DisplayTracker,
logger: SwipeUpGestureLogger,
-) : SwipeUpGestureHandler(context, logger, loggerTag = LOGGER_TAG) {
+) : SwipeUpGestureHandler(context, displayTracker, logger, loggerTag = LOGGER_TAG) {
private var viewFetcher: () -> View? = { null }
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
index 933c060..b1be404 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
@@ -21,6 +21,7 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
import dagger.Module
@@ -41,10 +42,11 @@
fun provideSwipeChipbarAwayGestureHandler(
mediaTttFlags: MediaTttFlags,
context: Context,
+ displayTracker: DisplayTracker,
logger: SwipeUpGestureLogger,
): SwipeChipbarAwayGestureHandler? {
return if (mediaTttFlags.isMediaTttDismissGestureEnabled()) {
- SwipeChipbarAwayGestureHandler(context, logger)
+ SwipeChipbarAwayGestureHandler(context, displayTracker, logger)
} else {
null
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 0069bb5..19a0866 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -38,6 +38,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.LinearLightRevealEffect
@@ -68,6 +69,7 @@
@Main private val executor: Executor,
private val threadFactory: ThreadFactory,
private val rotationChangeProvider: RotationChangeProvider,
+ private val displayTracker: DisplayTracker
) {
private val transitionListener = TransitionListener()
@@ -104,7 +106,7 @@
.setName("unfold-overlay-container")
displayAreaHelper.get().attachToRootDisplayArea(
- Display.DEFAULT_DISPLAY,
+ displayTracker.defaultDisplayId,
containerBuilder
) { builder ->
executor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index 977cf0e..08dbb81 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -49,14 +49,16 @@
/**
* Creates an intent to start this activity.
*/
- public static Intent createIntentForStart(Context context) {
+ public static Intent createIntentForStart(Context context, boolean isKeyguardShowing) {
Intent intent = new Intent(context, CreateUserActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(EXTRA_IS_KEYGUARD_SHOWING, isKeyguardShowing);
return intent;
}
private static final String TAG = "CreateUserActivity";
private static final String DIALOG_STATE_KEY = "create_user_dialog_state";
+ private static final String EXTRA_IS_KEYGUARD_SHOWING = "extra_is_keyguard_showing";
private final UserCreator mUserCreator;
private final EditUserInfoController mEditUserInfoController;
@@ -86,7 +88,11 @@
if (savedInstanceState != null) {
mEditUserInfoController.onRestoreInstanceState(savedInstanceState);
}
- if (mUserCreator.isMultipleAdminEnabled()) {
+ boolean isKeyguardShowing = getIntent().getBooleanExtra(EXTRA_IS_KEYGUARD_SHOWING, true);
+ // Display grant admin dialog only on unlocked device to admin users if multiple admins
+ // are allowed on this device.
+ if (mUserCreator.isMultipleAdminEnabled() && mUserCreator.isUserAdmin()
+ && !isKeyguardShowing) {
mGrantAdminDialog = buildGrantAdminDialog();
mGrantAdminDialog.show();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
index 1811c4d..5f89d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
@@ -87,6 +87,10 @@
userManager.setUserAdmin(userId)
}
+ fun isUserAdmin(): Boolean {
+ return userManager.isAdminUser
+ }
+
fun isMultipleAdminEnabled(): Boolean {
return UserManager.isMultipleAdminEnabled()
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
index 5a25a4e..fdf13be 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
@@ -70,7 +70,7 @@
)
context.startActivityAsUser(
- CreateUserActivity.createIntentForStart(context),
+ CreateUserActivity.createIntentForStart(context, isKeyguardShowing),
userHandle,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index d54de3fa..c2727fc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -14,8 +14,6 @@
package com.android.systemui.util;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import android.Manifest;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +24,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.QuickStepContract;
import java.util.List;
@@ -71,8 +70,9 @@
* {@link android.view.WindowManagerPolicyConstants#NAV_BAR_MODE_GESTURAL} AND
* the context is that of the default display
*/
- public static boolean isGesturalModeOnDefaultDisplay(Context context, int navMode) {
- return context.getDisplayId() == DEFAULT_DISPLAY
+ public static boolean isGesturalModeOnDefaultDisplay(Context context,
+ DisplayTracker displayTracker, int navMode) {
+ return context.getDisplayId() == displayTracker.getDefaultDisplayId()
&& QuickStepContract.isGesturalMode(navMode);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 31df3bc..76a01b9 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -16,6 +16,10 @@
package com.android.systemui.wallpapers;
+import static android.app.WallpaperManager.FLAG_LOCK;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.app.WallpaperManager.SetWallpaperFlags;
+
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.graphics.Bitmap;
@@ -165,7 +169,9 @@
}
mWallpaperManager = getDisplayContext().getSystemService(WallpaperManager.class);
mSurfaceHolder = surfaceHolder;
- Rect dimensions = mWallpaperManager.peekBitmapDimensions();
+ Rect dimensions = mWallpaperManager.isLockscreenLiveWallpaperEnabled()
+ ? mWallpaperManager.peekBitmapDimensions(getSourceFlag())
+ : mWallpaperManager.peekBitmapDimensions();
int width = Math.max(MIN_SURFACE_WIDTH, dimensions.width());
int height = Math.max(MIN_SURFACE_HEIGHT, dimensions.height());
mSurfaceHolder.setFixedSize(width, height);
@@ -314,7 +320,10 @@
boolean loadSuccess = false;
Bitmap bitmap;
try {
- bitmap = mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
+ bitmap = mWallpaperManager.isLockscreenLiveWallpaperEnabled()
+ ? mWallpaperManager.getBitmapAsUser(
+ mUserTracker.getUserId(), false, getSourceFlag())
+ : mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
if (bitmap != null
&& bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
throw new RuntimeException("Wallpaper is too large to draw!");
@@ -325,10 +334,18 @@
// be loaded, we will go into a cycle. Don't do a build where the
// default wallpaper can't be loaded.
Log.w(TAG, "Unable to load wallpaper!", exception);
- mWallpaperManager.clearWallpaper(
- WallpaperManager.FLAG_SYSTEM, mUserTracker.getUserId());
+ if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
+ mWallpaperManager.clearWallpaper(getWallpaperFlags(), mUserTracker.getUserId());
+ } else {
+ mWallpaperManager.clearWallpaper(
+ WallpaperManager.FLAG_SYSTEM, mUserTracker.getUserId());
+ }
+
try {
- bitmap = mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
+ bitmap = mWallpaperManager.isLockscreenLiveWallpaperEnabled()
+ ? mWallpaperManager.getBitmapAsUser(
+ mUserTracker.getUserId(), false, getSourceFlag())
+ : mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
} catch (RuntimeException | OutOfMemoryError e) {
Log.w(TAG, "Unable to load default wallpaper!", e);
bitmap = null;
@@ -349,8 +366,9 @@
mBitmap.recycle();
}
mBitmap = bitmap;
- mWideColorGamut = mWallpaperManager.wallpaperSupportsWcg(
- WallpaperManager.FLAG_SYSTEM);
+ mWideColorGamut = mWallpaperManager.isLockscreenLiveWallpaperEnabled()
+ ? mWallpaperManager.wallpaperSupportsWcg(getSourceFlag())
+ : mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM);
// +2 usages for the color extraction and the delayed unload.
mBitmapUsages += 2;
@@ -379,6 +397,15 @@
}
}
+ /**
+ * Helper to return the flag from where the source bitmap is from.
+ * Similar to {@link #getWallpaperFlags()}, but returns (FLAG_SYSTEM) instead of
+ * (FLAG_LOCK | FLAG_SYSTEM) if this engine is used for both lock screen & home screen.
+ */
+ private @SetWallpaperFlags int getSourceFlag() {
+ return getWallpaperFlags() == FLAG_LOCK ? FLAG_LOCK : FLAG_SYSTEM;
+ }
+
@VisibleForTesting
void recomputeColorExtractorMiniBitmap() {
mWallpaperLocalColorExtractor.onBitmapChanged(mBitmap);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 7033ccd..5d896cb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -236,7 +236,8 @@
// Store callback in a field so it won't get GC'd
mStatusBarWindowCallback =
- (keyguardShowing, keyguardOccluded, bouncerShowing, isDozing, panelExpanded) ->
+ (keyguardShowing, keyguardOccluded, bouncerShowing, isDozing, panelExpanded,
+ isDreaming) ->
mBubbles.onNotificationPanelExpandedChanged(panelExpanded);
notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 8ef98f0..bd60401 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -16,8 +16,6 @@
package com.android.systemui.wmshell;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
@@ -50,6 +48,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.notetask.NoteTaskInitializer;
+import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
@@ -124,6 +123,7 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final ProtoTracer mProtoTracer;
private final UserTracker mUserTracker;
+ private final DisplayTracker mDisplayTracker;
private final NoteTaskInitializer mNoteTaskInitializer;
private final Executor mSysUiMainExecutor;
@@ -186,6 +186,7 @@
ProtoTracer protoTracer,
WakefulnessLifecycle wakefulnessLifecycle,
UserTracker userTracker,
+ DisplayTracker displayTracker,
NoteTaskInitializer noteTaskInitializer,
@Main Executor sysUiMainExecutor) {
mContext = context;
@@ -203,6 +204,7 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mProtoTracer = protoTracer;
mUserTracker = userTracker;
+ mDisplayTracker = displayTracker;
mNoteTaskInitializer = noteTaskInitializer;
mSysUiMainExecutor = sysUiMainExecutor;
}
@@ -268,7 +270,7 @@
public void onStartTransition(boolean isEntering) {
mSysUiMainExecutor.execute(() -> {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
- true).commitUpdate(DEFAULT_DISPLAY);
+ true).commitUpdate(mDisplayTracker.getDefaultDisplayId());
});
}
@@ -276,7 +278,7 @@
public void onStartFinished(Rect bounds) {
mSysUiMainExecutor.execute(() -> {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
- true).commitUpdate(DEFAULT_DISPLAY);
+ true).commitUpdate(mDisplayTracker.getDefaultDisplayId());
});
}
@@ -284,7 +286,7 @@
public void onStopFinished(Rect bounds) {
mSysUiMainExecutor.execute(() -> {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
- false).commitUpdate(DEFAULT_DISPLAY);
+ false).commitUpdate(mDisplayTracker.getDefaultDisplayId());
});
}
});
@@ -333,7 +335,8 @@
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis,
int backDisposition, boolean showImeSwitcher) {
- if (displayId == DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) != 0) {
+ if (displayId == mDisplayTracker.getDefaultDisplayId()
+ && (vis & InputMethodService.IME_VISIBLE) != 0) {
oneHanded.stopOneHanded(
OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
}
@@ -346,7 +349,7 @@
@Override
public void onVisibilityChanged(boolean hasFreeformTasks) {
mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, hasFreeformTasks)
- .commitUpdate(DEFAULT_DISPLAY);
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
}
}, mSysUiMainExecutor);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index c76b127..00b2fbe 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -51,7 +51,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -140,8 +139,9 @@
@Test
fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) {
- verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
- verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
+ // TODO(b/266103601): delete this test and add more coverage for updateColors()
+ // verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+ // verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
@@ -152,9 +152,6 @@
@Test
fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) {
- verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
- verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
-
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
captor.value.onDensityOrFontScaleChanged()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index 01365b4..30e3d09 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -25,9 +25,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -39,6 +37,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.settings.FakeDisplayTracker;
import org.junit.Before;
import org.junit.Test;
@@ -58,12 +57,12 @@
@Mock
private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@Mock
- private DisplayManager mDisplayManager;
- @Mock
private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
+ private Executor mMainExecutor = Runnable::run;
private Executor mBackgroundExecutor = Runnable::run;
private KeyguardDisplayManager mManager;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
// The default and secondary displays are both in the default group
private Display mDefaultDisplay;
@@ -75,9 +74,9 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
- mKeyguardStatusViewComponentFactory, mBackgroundExecutor));
+ mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
+ mBackgroundExecutor));
doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
@@ -96,23 +95,21 @@
@Test
public void testShow_defaultDisplayOnly() {
- when(mDisplayManager.getDisplays()).thenReturn(new Display[]{mDefaultDisplay});
+ mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay});
mManager.show();
verify(mManager, never()).createPresentation(any());
}
@Test
public void testShow_includeSecondaryDisplay() {
- when(mDisplayManager.getDisplays()).thenReturn(
- new Display[]{mDefaultDisplay, mSecondaryDisplay});
+ mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
mManager.show();
verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
}
@Test
public void testShow_includeAlwaysUnlockedDisplay() {
- when(mDisplayManager.getDisplays()).thenReturn(
- new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay});
+ mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay});
mManager.show();
verify(mManager, never()).createPresentation(any());
@@ -120,7 +117,7 @@
@Test
public void testShow_includeSecondaryAndAlwaysUnlockedDisplays() {
- when(mDisplayManager.getDisplays()).thenReturn(
+ mDisplayTracker.setAllDisplays(
new Display[]{mDefaultDisplay, mSecondaryDisplay, mAlwaysUnlockedDisplay});
mManager.show();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 06082b6..68dc6c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -29,6 +29,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
@@ -52,6 +53,7 @@
private ConfigurationController mConfigurationController;
@Mock
private ActivityStarter mActivityStarter;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private DumpManager mDumpManager = new DumpManager();
private KeyguardSliceViewController mController;
@@ -63,7 +65,7 @@
when(mView.getContext()).thenReturn(mContext);
mController = new KeyguardSliceViewController(
mView, mActivityStarter, mConfigurationController,
- mTunerService, mDumpManager);
+ mTunerService, mDumpManager, mDisplayTracker);
mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 87dd6a4..4d95a22 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -18,7 +18,6 @@
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
@@ -42,7 +41,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -96,7 +94,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.service.trust.TrustAgentService;
import android.telephony.ServiceState;
@@ -239,8 +236,6 @@
@Mock
private UiEventLogger mUiEventLogger;
@Mock
- private PowerManager mPowerManager;
- @Mock
private GlobalSettings mGlobalSettings;
private FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
@Mock
@@ -700,7 +695,6 @@
setKeyguardBouncerVisibility(true);
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
- verify(mFaceManager).isHardwareDetected();
verify(mFaceManager, never()).hasEnrolledTemplates(anyInt());
}
@@ -834,6 +828,19 @@
}
@Test
+ public void doesNotTryToAuthenticateWhenKeyguardIsNotShowingButOccluded_whenAssistant() {
+ mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
+ mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+ verify(mFaceManager, never()).authenticate(any(),
+ any(),
+ any(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ }
+
+ @Test
public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
@@ -846,6 +853,32 @@
}
@Test
+ public void faceUnlockDoesNotRunWhenDeviceIsGoingToSleepWithAssistantVisible() {
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
+ mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+ mTestableLooper.processAllMessages();
+ clearInvocations(mFaceManager);
+
+ // Device going to sleep while assistant is visible
+ mKeyguardUpdateMonitor.handleStartedGoingToSleep(0);
+ mKeyguardUpdateMonitor.handleFinishedGoingToSleep(0);
+ mTestableLooper.moveTimeForward(DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+ mTestableLooper.processAllMessages();
+
+ mKeyguardUpdateMonitor.handleKeyguardReset();
+
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
+ verify(mFaceManager, never()).authenticate(any(),
+ any(),
+ any(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ }
+
+ @Test
public void testIgnoresAuth_whenTrustAgentOnKeyguard_withoutBypass() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
@@ -1872,28 +1905,6 @@
}
@Test
- public void testFingerAcquired_wakesUpPowerManager() {
- cleanupKeyguardUpdateMonitor();
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.kg_wake_on_acquire_start, true);
- mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
- fingerprintAcquireStart();
-
- verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
- }
-
- @Test
- public void testFingerAcquired_doesNotWakeUpPowerManager() {
- cleanupKeyguardUpdateMonitor();
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.kg_wake_on_acquire_start, false);
- mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
- fingerprintAcquireStart();
-
- verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
- }
-
- @Test
public void testDreamingStopped_faceDoesNotRun() {
mKeyguardUpdateMonitor.dispatchDreamingStopped();
mTestableLooper.processAllMessages();
@@ -2374,11 +2385,6 @@
.onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
}
- private void fingerprintAcquireStart() {
- mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
- .onAuthenticationAcquired(FINGERPRINT_ACQUIRED_START);
- }
-
private void deviceInPostureStateOpened() {
mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED);
}
@@ -2525,7 +2531,7 @@
mAuthController, mTelephonyListenerManager,
mInteractionJankMonitor, mLatencyTracker, mActiveUnlockConfig,
mKeyguardUpdateMonitorLogger, mUiEventLogger, () -> mSessionTracker,
- mPowerManager, mTrustManager, mSubscriptionManager, mUserManager,
+ mTrustManager, mSubscriptionManager, mUserManager,
mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 0627fc6..e918c1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -89,6 +89,7 @@
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegate;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.tuner.TunerService;
@@ -117,6 +118,7 @@
private DisplayManager mDisplayManager;
private SecureSettings mSecureSettings;
private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private FakeThreadFactory mThreadFactory;
private ArrayList<DecorProvider> mPrivacyDecorProviders;
private ArrayList<DecorProvider> mFaceScanningProviders;
@@ -220,7 +222,7 @@
mExecutor));
mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
- mTunerService, mUserTracker, mDotViewController, mThreadFactory,
+ mTunerService, mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
@Override
public void start() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 47dff51..213ce9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -39,6 +39,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
@@ -79,6 +80,7 @@
private IWindowMagnificationConnection mIWindowMagnificationConnection;
private WindowMagnification mWindowMagnification;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@Before
public void setUp() throws Exception {
@@ -91,7 +93,8 @@
any(IWindowMagnificationConnection.class));
mWindowMagnification = new WindowMagnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue,
- mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings);
+ mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
+ mDisplayTracker);
mWindowMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 89ab835..201f020 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -87,6 +87,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.leak.ReferenceTestUtils;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.utils.os.FakeHandler;
@@ -131,12 +132,13 @@
private Handler mHandler;
private TestableWindowManager mWindowManager;
- private SysUiState mSysUiState = new SysUiState();
+ private SysUiState mSysUiState;
private Resources mResources;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
+ private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private IWindowSession mWindowSessionSpy;
@@ -162,6 +164,7 @@
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(FrameCallback.class));
+ mSysUiState = new SysUiState(mDisplayTracker);
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
returnsSecondArg());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 14b00ca..583f2db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
@@ -77,6 +78,8 @@
private CommandQueue mCommandQueue;
private WindowMagnification mWindowMagnification;
private OverviewProxyListener mOverviewProxyListener;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -90,10 +93,10 @@
when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
- mCommandQueue = new CommandQueue(getContext());
+ mCommandQueue = new CommandQueue(getContext(), mDisplayTracker);
mWindowMagnification = new WindowMagnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
- mSysUiState, mOverviewProxyService, mSecureSettings);
+ mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker);
mWindowMagnification.start();
final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 15a3145..aac0b51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
@@ -353,13 +354,14 @@
private AccessibilityFloatingMenuController setUpController(FakeFeatureFlags featureFlags) {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ final FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class));
mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver,
- mKeyguardUpdateMonitor, featureFlags, mSecureSettings);
+ mKeyguardUpdateMonitor, featureFlags, mSecureSettings, displayTracker);
controller.init();
return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
new file mode 100644
index 0000000..3bdbf97
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
@@ -0,0 +1,87 @@
+package com.android.systemui.animation.back
+
+import android.util.DisplayMetrics
+import android.window.BackEvent
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private data class BackInput(val progressX: Float, val progressY: Float, val edge: Int)
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BackAnimationSpecTest : SysuiTestCase() {
+ private var displayMetrics =
+ DisplayMetrics().apply {
+ widthPixels = 100
+ heightPixels = 200
+ density = 3f
+ }
+
+ @Test
+ fun sysUi_floatingSystemSurfaces_animationValues() {
+ val maxX = 14.0f
+ val maxY = 4.0f
+ val minScale = 0.8f
+
+ val backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics)
+
+ assertBackTransformation(
+ backAnimationSpec = backAnimationSpec,
+ backInput = BackInput(progressX = 0f, progressY = 0f, edge = BackEvent.EDGE_LEFT),
+ expected = BackTransformation(translateX = 0f, translateY = 0f, scale = 1f),
+ )
+ assertBackTransformation(
+ backAnimationSpec = backAnimationSpec,
+ backInput = BackInput(progressX = 1f, progressY = 0f, edge = BackEvent.EDGE_LEFT),
+ expected = BackTransformation(translateX = -maxX, translateY = 0f, scale = minScale),
+ )
+ assertBackTransformation(
+ backAnimationSpec = backAnimationSpec,
+ backInput = BackInput(progressX = 1f, progressY = 0f, edge = BackEvent.EDGE_RIGHT),
+ expected = BackTransformation(translateX = maxX, translateY = 0f, scale = minScale),
+ )
+ assertBackTransformation(
+ backAnimationSpec = backAnimationSpec,
+ backInput = BackInput(progressX = 1f, progressY = 1f, edge = BackEvent.EDGE_LEFT),
+ expected = BackTransformation(translateX = -maxX, translateY = -maxY, scale = minScale),
+ )
+ assertBackTransformation(
+ backAnimationSpec = backAnimationSpec,
+ backInput = BackInput(progressX = 0f, progressY = 1f, edge = BackEvent.EDGE_LEFT),
+ expected = BackTransformation(translateX = 0f, translateY = -maxY, scale = 1f),
+ )
+ assertBackTransformation(
+ backAnimationSpec = backAnimationSpec,
+ backInput = BackInput(progressX = 0f, progressY = -1f, edge = BackEvent.EDGE_LEFT),
+ expected = BackTransformation(translateX = 0f, translateY = maxY, scale = 1f),
+ )
+ }
+}
+
+private fun assertBackTransformation(
+ backAnimationSpec: BackAnimationSpec,
+ backInput: BackInput,
+ expected: BackTransformation,
+) {
+ val actual = BackTransformation()
+ backAnimationSpec.getBackTransformation(
+ backEvent =
+ BackEvent(
+ /* touchX = */ 0f,
+ /* touchY = */ 0f,
+ /* progress = */ backInput.progressX,
+ /* swipeEdge = */ backInput.edge,
+ ),
+ progressY = backInput.progressY,
+ result = actual
+ )
+
+ val tolerance = 0f
+ assertThat(actual.translateX).isWithin(tolerance).of(expected.translateX)
+ assertThat(actual.translateY).isWithin(tolerance).of(expected.translateY)
+ assertThat(actual.scale).isWithin(tolerance).of(expected.scale)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
new file mode 100644
index 0000000..190b3d2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
@@ -0,0 +1,80 @@
+package com.android.systemui.animation.back
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BackTransformationTest : SysuiTestCase() {
+ private val targetView: View = mock()
+
+ @Test
+ fun defaultValue_noTransformation() {
+ val transformation = BackTransformation()
+
+ assertThat(transformation.translateX).isNaN()
+ assertThat(transformation.translateY).isNaN()
+ assertThat(transformation.scale).isNaN()
+ }
+
+ @Test
+ fun applyTo_targetView_translateX_Y_Scale() {
+ val transformation = BackTransformation(translateX = 0f, translateY = 0f, scale = 1f)
+
+ transformation.applyTo(targetView = targetView)
+
+ verify(targetView).translationX = 0f
+ verify(targetView).translationY = 0f
+ verify(targetView).scaleX = 1f
+ verify(targetView).scaleY = 1f
+ verifyNoMoreInteractions(targetView)
+ }
+
+ @Test
+ fun applyTo_targetView_translateX() {
+ val transformation = BackTransformation(translateX = 1f)
+
+ transformation.applyTo(targetView = targetView)
+
+ verify(targetView).translationX = 1f
+ verifyNoMoreInteractions(targetView)
+ }
+
+ @Test
+ fun applyTo_targetView_translateY() {
+ val transformation = BackTransformation(translateY = 2f)
+
+ transformation.applyTo(targetView = targetView)
+
+ verify(targetView).translationY = 2f
+ verifyNoMoreInteractions(targetView)
+ }
+
+ @Test
+ fun applyTo_targetView_scale() {
+ val transformation = BackTransformation(scale = 3f)
+
+ transformation.applyTo(targetView = targetView)
+
+ verify(targetView).scaleX = 3f
+ verify(targetView).scaleY = 3f
+ verifyNoMoreInteractions(targetView)
+ }
+
+ @Test
+ fun applyTo_targetView_noTransformation() {
+ val transformation = BackTransformation()
+
+ transformation.applyTo(targetView = targetView)
+
+ verifyNoMoreInteractions(targetView)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
new file mode 100644
index 0000000..921f9a8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
@@ -0,0 +1,63 @@
+package com.android.systemui.animation.back
+
+import android.util.DisplayMetrics
+import android.window.BackEvent
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(JUnit4::class)
+class OnBackAnimationCallbackExtensionTest : SysuiTestCase() {
+ private val onBackProgress: (BackTransformation) -> Unit = mock()
+ private val onBackStart: (BackEvent) -> Unit = mock()
+ private val onBackInvoke: () -> Unit = mock()
+ private val onBackCancel: () -> Unit = mock()
+
+ private val displayMetrics =
+ DisplayMetrics().apply {
+ widthPixels = 100
+ heightPixels = 100
+ density = 1f
+ }
+
+ private val onBackAnimationCallback =
+ onBackAnimationCallbackFrom(
+ backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics),
+ displayMetrics = displayMetrics,
+ onBackProgressed = onBackProgress,
+ onBackStarted = onBackStart,
+ onBackInvoked = onBackInvoke,
+ onBackCancelled = onBackCancel,
+ )
+
+ @Test
+ fun onBackProgressed_shouldInvoke_onBackProgress() {
+ val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT)
+ onBackAnimationCallback.onBackStarted(backEvent)
+
+ onBackAnimationCallback.onBackProgressed(backEvent)
+
+ verify(onBackProgress).invoke(BackTransformation(0f, 0f, 1f))
+ }
+
+ @Test
+ fun onBackStarted_shouldInvoke_onBackStart() {
+ val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT)
+
+ onBackAnimationCallback.onBackStarted(backEvent)
+
+ verify(onBackStart).invoke(backEvent)
+ }
+
+ @Test
+ fun onBackInvoked_shouldInvoke_onBackInvoke() {
+ onBackAnimationCallback.onBackInvoked()
+
+ verify(onBackInvoke).invoke()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index b92c5d0..fd931b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -51,14 +51,24 @@
import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.MODERN_ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.data.repository.FakeBiometricRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestCoroutineScope
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -101,6 +111,9 @@
@Captor lateinit var overlayCaptor: ArgumentCaptor<View>
@Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
+ private lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
+ private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ private val featureFlags = FakeFeatureFlags()
private val executor = FakeExecutor(FakeSystemClock())
private lateinit var overlayController: ISidefpsController
private lateinit var sideFpsController: SideFpsController
@@ -121,6 +134,24 @@
@Before
fun setup() {
+ featureFlags.set(MODERN_ALTERNATE_BOUNCER, true)
+ keyguardBouncerRepository =
+ KeyguardBouncerRepository(
+ mock(ViewMediatorCallback::class.java),
+ FakeSystemClock(),
+ TestCoroutineScope(),
+ mock(TableLogBuffer::class.java),
+ )
+ alternateBouncerInteractor =
+ AlternateBouncerInteractor(
+ keyguardBouncerRepository,
+ FakeBiometricRepository(),
+ FakeDeviceEntryFingerprintAuthRepository(),
+ FakeSystemClock(),
+ mock(KeyguardUpdateMonitor::class.java),
+ featureFlags,
+ )
+
context.addMockSystemService(DisplayManager::class.java, displayManager)
context.addMockSystemService(WindowManager::class.java, windowManager)
@@ -217,7 +248,10 @@
displayManager,
executor,
handler,
- dumpManager
+ alternateBouncerInteractor,
+ TestCoroutineScope(),
+ featureFlags,
+ dumpManager,
)
overlayController =
@@ -507,6 +541,26 @@
private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) {
sideFpsController.overlayOffsets = sensorLocation
+ }
+
+ fun alternateBouncerVisibility_showAndHideSideFpsUI() = testWithDisplay {
+ // WHEN alternate bouncer is visible
+ keyguardBouncerRepository.setAlternateVisible(true)
+ executor.runAllReady()
+
+ // THEN side fps shows UI
+ verify(windowManager).addView(any(), any())
+ verify(windowManager, never()).removeView(any())
+
+ // WHEN alternate bouncer is no longer visible
+ keyguardBouncerRepository.setAlternateVisible(false)
+ executor.runAllReady()
+
+ // THEN side fps UI is hidden
+ verify(windowManager).removeView(any())
+ }
+
+ private fun hidesWithTaskbar(visible: Boolean) {
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
@@ -515,7 +569,7 @@
verify(windowManager).addView(any(), any())
verify(windowManager, never()).removeView(any())
- verify(sideFpsView).visibility = if (sfpsViewVisible) View.VISIBLE else View.GONE
+ verify(sideFpsView).visibility = if (visible) View.VISIBLE else View.GONE
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index b9a952a..36ed6d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -105,6 +105,7 @@
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
@Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
+ @Mock private lateinit var udfpsKeyguardView: UdfpsKeyguardView
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@@ -125,7 +126,7 @@
whenever(inflater.inflate(R.layout.udfps_bp_view, null))
.thenReturn(mock(UdfpsBpView::class.java))
whenever(inflater.inflate(R.layout.udfps_keyguard_view, null))
- .thenReturn(mock(UdfpsKeyguardView::class.java))
+ .thenReturn(udfpsKeyguardView)
whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null))
.thenReturn(mock(UdfpsFpmEmptyView::class.java))
whenever(udfpsEnrollView.context).thenReturn(context)
@@ -152,7 +153,10 @@
fun showUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { showUdfpsOverlay() }
@Test
- fun showUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) { showUdfpsOverlay() }
+ fun showUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) {
+ showUdfpsOverlay()
+ verify(udfpsKeyguardView).updateSensorLocation(eq(overlayParams.sensorBounds))
+ }
@Test
fun showUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { showUdfpsOverlay() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
index 9c32c38..498cc29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
@@ -37,7 +37,6 @@
import com.android.systemui.shade.ShadeExpansionListener;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -71,7 +70,6 @@
protected @Mock SystemUIDialogManager mDialogManager;
protected @Mock UdfpsController mUdfpsController;
protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
- protected @Mock KeyguardBouncer mBouncer;
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -152,8 +150,6 @@
mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer);
mFeatureFlags.set(Flags.MODERN_ALTERNATE_BOUNCER, useModernBouncer);
mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, useExpandedOverlay);
- when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(
- useModernBouncer ? null : mBouncer);
UdfpsKeyguardViewController controller = new UdfpsKeyguardViewController(
mView,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 7715f7f..f437a8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -32,24 +32,17 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.shade.ShadeExpansionListener;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest {
- private @Captor ArgumentCaptor<PrimaryBouncerExpansionCallback>
- mBouncerExpansionCallbackCaptor;
- private PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
-
@Override
public UdfpsKeyguardViewController createUdfpsKeyguardViewController() {
return createUdfpsKeyguardViewController(/* useModernBouncer */ false,
@@ -62,11 +55,9 @@
captureStatusBarStateListeners();
sendStatusBarStateChanged(StatusBarState.KEYGUARD);
- captureBouncerExpansionCallback();
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
- mBouncerExpansionCallback.onVisibilityChanged(true);
-
+ when(mView.getUnpausedAlpha()).thenReturn(0);
assertTrue(mController.shouldPauseAuth());
}
@@ -304,11 +295,6 @@
verify(mView, atLeastOnce()).setPauseAuth(false);
}
- private void captureBouncerExpansionCallback() {
- verify(mBouncer).addBouncerExpansionCallback(mBouncerExpansionCallbackCaptor.capture());
- mBouncerExpansionCallback = mBouncerExpansionCallbackCaptor.getValue();
- }
-
@Test
// TODO(b/259264861): Tracking Bug
public void testUdfpsExpandedOverlayOn() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 9060922..81a6bc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.BiometricRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -91,6 +92,7 @@
AlternateBouncerInteractor(
keyguardBouncerRepository,
mock(BiometricRepository::class.java),
+ mock(DeviceEntryFingerprintAuthRepository::class.java),
mock(SystemClock::class.java),
mock(KeyguardUpdateMonitor::class.java),
mock(FeatureFlags::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
index 34ddf79..8e20303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -110,6 +110,28 @@
expectedInteractionEvent = InteractionEvent.UP,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
+ // MotionEvent.ACTION_HOVER_ENTER
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID_1,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
// MotionEvent.ACTION_MOVE
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_MOVE,
@@ -161,6 +183,35 @@
expectedInteractionEvent = InteractionEvent.UNCHANGED,
expectedPointerOnSensorId = POINTER_ID_2,
),
+ // MotionEvent.ACTION_HOVER_MOVE
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID_1,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = POINTER_ID_1,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
// MotionEvent.ACTION_UP
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_UP,
@@ -183,6 +234,28 @@
expectedInteractionEvent = InteractionEvent.UNCHANGED,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
+ // MotionEvent.ACTION_HOVER_EXIT
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
// MotionEvent.ACTION_CANCEL
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_CANCEL,
@@ -315,13 +388,7 @@
expectedPointerOnSensorId = POINTER_ID_2
)
)
- .flatten() +
- listOf(
- genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_ENTER),
- genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_MOVE),
- genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT)
- )
- .flatten()
+ .flatten()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
index bdd496e..71c335e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.clipboardoverlay;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
-
import static com.google.android.setupcompat.util.WizardManagerHelper.SETTINGS_SECURE_USER_SETUP_COMPLETE;
import static org.junit.Assert.assertEquals;
@@ -33,7 +31,6 @@
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.os.PersistableBundle;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import androidx.test.filters.SmallTest;
@@ -41,9 +38,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.util.DeviceConfigProxyFake;
import org.junit.Before;
import org.junit.Test;
@@ -63,18 +57,11 @@
@Mock
private ClipboardManager mClipboardManager;
@Mock
- private ClipboardOverlayControllerLegacyFactory mClipboardOverlayControllerLegacyFactory;
- @Mock
- private ClipboardOverlayControllerLegacy mOverlayControllerLegacy;
- @Mock
private ClipboardOverlayController mOverlayController;
@Mock
private ClipboardToast mClipboardToast;
@Mock
private UiEventLogger mUiEventLogger;
- @Mock
- private FeatureFlags mFeatureFlags;
- private DeviceConfigProxyFake mDeviceConfigProxy;
private ClipData mSampleClipData;
private String mSampleSource = "Example source";
@@ -97,8 +84,6 @@
mOverlayControllerProvider = () -> mOverlayController;
MockitoAnnotations.initMocks(this);
- when(mClipboardOverlayControllerLegacyFactory.create(any()))
- .thenReturn(mOverlayControllerLegacy);
when(mClipboardManager.hasPrimaryClip()).thenReturn(true);
Settings.Secure.putInt(
mContext.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 1);
@@ -108,26 +93,13 @@
when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData);
when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
- mDeviceConfigProxy = new DeviceConfigProxyFake();
-
- mClipboardListener = new ClipboardListener(getContext(), mDeviceConfigProxy,
- mOverlayControllerProvider, mClipboardOverlayControllerLegacyFactory,
- mClipboardToast, mClipboardManager, mUiEventLogger, mFeatureFlags);
+ mClipboardListener = new ClipboardListener(getContext(), mOverlayControllerProvider,
+ mClipboardToast, mClipboardManager, mUiEventLogger);
}
- @Test
- public void test_disabled() {
- mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
- "false", false);
- mClipboardListener.start();
- verifyZeroInteractions(mClipboardManager);
- verifyZeroInteractions(mUiEventLogger);
- }
@Test
- public void test_enabled() {
- mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
- "true", false);
+ public void test_initialization() {
mClipboardListener.start();
verify(mClipboardManager).addPrimaryClipChangedListener(any());
verifyZeroInteractions(mUiEventLogger);
@@ -135,45 +107,6 @@
@Test
public void test_consecutiveCopies() {
- when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(false);
-
- mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
- "true", false);
- mClipboardListener.start();
- mClipboardListener.onPrimaryClipChanged();
-
- verify(mClipboardOverlayControllerLegacyFactory).create(any());
-
- verify(mOverlayControllerLegacy).setClipData(
- mClipDataCaptor.capture(), mStringCaptor.capture());
-
- assertEquals(mSampleClipData, mClipDataCaptor.getValue());
- assertEquals(mSampleSource, mStringCaptor.getValue());
-
- verify(mOverlayControllerLegacy).setOnSessionCompleteListener(mRunnableCaptor.capture());
-
- // Should clear the overlay controller
- mRunnableCaptor.getValue().run();
-
- mClipboardListener.onPrimaryClipChanged();
-
- verify(mClipboardOverlayControllerLegacyFactory, times(2)).create(any());
-
- // Not calling the runnable here, just change the clip again and verify that the overlay is
- // NOT recreated.
-
- mClipboardListener.onPrimaryClipChanged();
-
- verify(mClipboardOverlayControllerLegacyFactory, times(2)).create(any());
- verifyZeroInteractions(mOverlayControllerProvider);
- }
-
- @Test
- public void test_consecutiveCopies_new() {
- when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(true);
-
- mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
- "true", false);
mClipboardListener.start();
mClipboardListener.onPrimaryClipChanged();
@@ -200,7 +133,6 @@
mClipboardListener.onPrimaryClipChanged();
verify(mOverlayControllerProvider, times(2)).get();
- verifyZeroInteractions(mClipboardOverlayControllerLegacyFactory);
}
@Test
@@ -231,23 +163,6 @@
@Test
public void test_logging_enterAndReenter() {
- when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(false);
-
- mClipboardListener.start();
-
- mClipboardListener.onPrimaryClipChanged();
- mClipboardListener.onPrimaryClipChanged();
-
- verify(mUiEventLogger, times(1)).log(
- ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource);
- verify(mUiEventLogger, times(1)).log(
- ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED, 0, mSampleSource);
- }
-
- @Test
- public void test_logging_enterAndReenter_new() {
- when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(true);
-
mClipboardListener.start();
mClipboardListener.onPrimaryClipChanged();
@@ -271,6 +186,5 @@
ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource);
verify(mClipboardToast, times(1)).showCopiedToast();
verifyZeroInteractions(mOverlayControllerProvider);
- verifyZeroInteractions(mClipboardOverlayControllerLegacyFactory);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index b4e85c0..ca5b7af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.screenshot.TimeoutHandler;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -81,6 +82,7 @@
private ClipboardOverlayUtils mClipboardUtils;
@Mock
private UiEventLogger mUiEventLogger;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock
@@ -116,7 +118,8 @@
mFeatureFlags,
mClipboardUtils,
mExecutor,
- mUiEventLogger);
+ mUiEventLogger,
+ mDisplayTracker);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
new file mode 100644
index 0000000..fe352fd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.common.ui.view
+
+import android.view.ViewConfiguration
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel.Down
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel.Move
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel.Up
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() {
+
+ @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle
+ @Mock private lateinit var onLongPressDetected: (Int, Int) -> Unit
+ @Mock private lateinit var onSingleTapDetected: () -> Unit
+
+ private lateinit var underTest: LongPressHandlingViewInteractionHandler
+
+ private var isAttachedToWindow: Boolean = true
+ private var delayedRunnable: Runnable? = null
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(postDelayed.invoke(any(), any())).thenAnswer { invocation ->
+ delayedRunnable = invocation.arguments[0] as Runnable
+ DisposableHandle { delayedRunnable = null }
+ }
+
+ underTest =
+ LongPressHandlingViewInteractionHandler(
+ postDelayed = postDelayed,
+ isAttachedToWindow = { isAttachedToWindow },
+ onLongPressDetected = onLongPressDetected,
+ onSingleTapDetected = onSingleTapDetected,
+ )
+ underTest.isLongPressHandlingEnabled = true
+ }
+
+ @Test
+ fun `long-press`() = runTest {
+ val downX = 123
+ val downY = 456
+ dispatchTouchEvents(
+ Down(
+ x = downX,
+ y = downY,
+ ),
+ Move(
+ distanceMoved = ViewConfiguration.getTouchSlop() - 0.1f,
+ ),
+ )
+ delayedRunnable?.run()
+
+ verify(onLongPressDetected).invoke(downX, downY)
+ verify(onSingleTapDetected, never()).invoke()
+ }
+
+ @Test
+ fun `long-press but feature not enabled`() = runTest {
+ underTest.isLongPressHandlingEnabled = false
+ dispatchTouchEvents(
+ Down(
+ x = 123,
+ y = 456,
+ ),
+ )
+
+ assertThat(delayedRunnable).isNull()
+ verify(onLongPressDetected, never()).invoke(any(), any())
+ verify(onSingleTapDetected, never()).invoke()
+ }
+
+ @Test
+ fun `long-press but view not attached`() = runTest {
+ isAttachedToWindow = false
+ dispatchTouchEvents(
+ Down(
+ x = 123,
+ y = 456,
+ ),
+ )
+ delayedRunnable?.run()
+
+ verify(onLongPressDetected, never()).invoke(any(), any())
+ verify(onSingleTapDetected, never()).invoke()
+ }
+
+ @Test
+ fun `dragged too far to be considered a long-press`() = runTest {
+ dispatchTouchEvents(
+ Down(
+ x = 123,
+ y = 456,
+ ),
+ Move(
+ distanceMoved = ViewConfiguration.getTouchSlop() + 0.1f,
+ ),
+ )
+
+ assertThat(delayedRunnable).isNull()
+ verify(onLongPressDetected, never()).invoke(any(), any())
+ verify(onSingleTapDetected, never()).invoke()
+ }
+
+ @Test
+ fun `held down too briefly to be considered a long-press`() = runTest {
+ dispatchTouchEvents(
+ Down(
+ x = 123,
+ y = 456,
+ ),
+ Up(
+ distanceMoved = ViewConfiguration.getTouchSlop().toFloat(),
+ gestureDuration = ViewConfiguration.getLongPressTimeout() - 1L,
+ ),
+ )
+
+ assertThat(delayedRunnable).isNull()
+ verify(onLongPressDetected, never()).invoke(any(), any())
+ verify(onSingleTapDetected).invoke()
+ }
+
+ private fun dispatchTouchEvents(
+ vararg models: MotionEventModel,
+ ) {
+ models.forEach { model -> underTest.onTouchEvent(model) }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index 25f471b..d54babf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dump.DumpManager
import com.android.systemui.settings.UserFileManager
@@ -66,6 +67,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
import org.mockito.MockitoAnnotations
@SmallTest
@@ -88,6 +90,8 @@
private lateinit var userTracker: UserTracker
@Mock
private lateinit var userFileManager: UserFileManager
+ @Mock
+ private lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
@Captor
private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo>
@@ -168,6 +172,7 @@
listingController,
userFileManager,
userTracker,
+ authorizedPanelsRepository,
Optional.of(persistenceWrapper),
mock(DumpManager::class.java)
)
@@ -224,6 +229,7 @@
listingController,
userFileManager,
userTracker,
+ authorizedPanelsRepository,
Optional.of(persistenceWrapper),
mock(DumpManager::class.java)
)
@@ -231,6 +237,26 @@
}
@Test
+ fun testAddAuthorizedPackagesFromSavedFavoritesOnStart() {
+ clearInvocations(authorizedPanelsRepository)
+ `when`(persistenceWrapper.readFavorites()).thenReturn(listOf(TEST_STRUCTURE_INFO))
+ ControlsControllerImpl(
+ mContext,
+ delayableExecutor,
+ uiController,
+ bindingController,
+ listingController,
+ userFileManager,
+ userTracker,
+ authorizedPanelsRepository,
+ Optional.of(persistenceWrapper),
+ mock(DumpManager::class.java)
+ )
+ verify(authorizedPanelsRepository)
+ .addAuthorizedPanels(setOf(TEST_STRUCTURE_INFO.componentName.packageName))
+ }
+
+ @Test
fun testOnActionResponse() {
controller.onActionResponse(TEST_COMPONENT, TEST_CONTROL_ID, ControlAction.RESPONSE_OK)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
index 765c4c0..226ef3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
@@ -21,12 +21,15 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.settingslib.core.lifecycle.Lifecycle
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -36,8 +39,10 @@
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.text.Collator
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -49,25 +54,18 @@
@Mock lateinit var lifecycle: Lifecycle
@Mock lateinit var controlsListingController: ControlsListingController
@Mock lateinit var layoutInflater: LayoutInflater
- @Mock lateinit var onAppSelected: (ComponentName?) -> Unit
+ @Mock lateinit var onAppSelected: (ControlsServiceInfo) -> Unit
@Mock lateinit var favoritesRenderer: FavoritesRenderer
val resources: Resources = context.resources
lateinit var adapter: AppAdapter
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- adapter = AppAdapter(backgroundExecutor,
- uiExecutor,
- lifecycle,
- controlsListingController,
- layoutInflater,
- onAppSelected,
- favoritesRenderer,
- resources)
}
@Test
fun testOnServicesUpdated_nullLoadLabel() {
+ adapter = createAdapterWithAuthorizedPanels(emptySet())
val captor = ArgumentCaptor
.forClass(ControlsListingController.ControlsListingCallback::class.java)
val controlsServiceInfo = mock<ControlsServiceInfo>()
@@ -76,14 +74,14 @@
verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
captor.value.onServicesUpdated(serviceInfo)
- backgroundExecutor.runAllReady()
- uiExecutor.runAllReady()
+ FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
assertThat(adapter.itemCount).isEqualTo(serviceInfo.size)
}
@Test
- fun testOnServicesUpdatedDoesntHavePanels() {
+ fun testOnServicesUpdated_showsNotAuthorizedPanels() {
+ adapter = createAdapterWithAuthorizedPanels(emptySet())
val captor = ArgumentCaptor
.forClass(ControlsListingController.ControlsListingCallback::class.java)
val serviceInfo = listOf(
@@ -93,20 +91,88 @@
verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
captor.value.onServicesUpdated(serviceInfo)
- backgroundExecutor.runAllReady()
- uiExecutor.runAllReady()
+ FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
+
+ assertThat(adapter.itemCount).isEqualTo(2)
+ }
+
+ @Test
+ fun testOnServicesUpdated_doesntShowAuthorizedPanels() {
+ adapter = createAdapterWithAuthorizedPanels(setOf(TEST_PACKAGE))
+
+ val captor = ArgumentCaptor
+ .forClass(ControlsListingController.ControlsListingCallback::class.java)
+ val serviceInfo = listOf(
+ ControlsServiceInfo("no panel", null),
+ ControlsServiceInfo("panel", ComponentName(TEST_PACKAGE, "cls"))
+ )
+ verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
+
+ captor.value.onServicesUpdated(serviceInfo)
+ FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
assertThat(adapter.itemCount).isEqualTo(1)
}
- fun ControlsServiceInfo(
- label: CharSequence,
- panelComponentName: ComponentName? = null
- ): ControlsServiceInfo {
- return mock {
- `when`(this.loadLabel()).thenReturn(label)
- `when`(this.panelActivity).thenReturn(panelComponentName)
- `when`(this.loadIcon()).thenReturn(mock())
+ @Test
+ fun testOnBindSetsClickListenerToCallOnAppSelected() {
+ adapter = createAdapterWithAuthorizedPanels(emptySet())
+
+ val captor = ArgumentCaptor
+ .forClass(ControlsListingController.ControlsListingCallback::class.java)
+ val serviceInfo = listOf(
+ ControlsServiceInfo("no panel", null),
+ ControlsServiceInfo("panel", ComponentName(TEST_PACKAGE, "cls"))
+ )
+ verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
+
+ captor.value.onServicesUpdated(serviceInfo)
+ FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
+
+ val sorted = serviceInfo.sortedWith(
+ compareBy(Collator.getInstance(resources.configuration.locales[0])) {
+ it.loadLabel() ?: ""
+ })
+
+ sorted.forEachIndexed { index, info ->
+ val fakeView: View = mock()
+ val fakeHolder: AppAdapter.Holder = mock()
+ `when`(fakeHolder.view).thenReturn(fakeView)
+
+ clearInvocations(onAppSelected)
+ adapter.onBindViewHolder(fakeHolder, index)
+ val listenerCaptor: ArgumentCaptor<View.OnClickListener> = argumentCaptor()
+ verify(fakeView).setOnClickListener(capture(listenerCaptor))
+ listenerCaptor.value.onClick(fakeView)
+
+ verify(onAppSelected).invoke(info)
}
}
+
+ private fun createAdapterWithAuthorizedPanels(packages: Set<String>): AppAdapter {
+ return AppAdapter(backgroundExecutor,
+ uiExecutor,
+ lifecycle,
+ controlsListingController,
+ layoutInflater,
+ onAppSelected,
+ favoritesRenderer,
+ resources,
+ packages)
+ }
+
+ companion object {
+ private fun ControlsServiceInfo(
+ label: CharSequence,
+ panelComponentName: ComponentName? = null
+ ): ControlsServiceInfo {
+ return mock {
+ `when`(loadLabel()).thenReturn(label)
+ `when`(panelActivity).thenReturn(panelComponentName)
+ `when`(loadIcon()).thenReturn(mock())
+ }
+ }
+
+ private const val TEST_PACKAGE = "package"
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
index 56c3efe..8dfd223 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -16,7 +16,13 @@
package com.android.systemui.controls.management
+import android.app.Dialog
+import android.content.ComponentName
import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.ServiceInfo
+import android.graphics.drawable.Drawable
+import android.os.Bundle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.window.OnBackInvokedCallback
@@ -25,14 +31,23 @@
import androidx.test.rule.ActivityTestRule
import androidx.test.runner.intercepting.SingleActivityFactory
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
+import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -41,7 +56,11 @@
import org.mockito.ArgumentMatchers
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
@SmallTest
@@ -58,9 +77,10 @@
@Mock lateinit var userTracker: UserTracker
- @Mock lateinit var uiController: ControlsUiController
+ @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
- private lateinit var controlsProviderSelectorActivity: ControlsProviderSelectorActivity_Factory
+ @Mock lateinit var dialogFactory: PanelConfirmationDialogFactory
+
private var latch: CountDownLatch = CountDownLatch(1)
@Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
@@ -81,7 +101,8 @@
listingController,
controlsController,
userTracker,
- uiController,
+ authorizedPanelsRepository,
+ dialogFactory,
mockDispatcher,
latch
)
@@ -113,13 +134,99 @@
verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
}
- public class TestableControlsProviderSelectorActivity(
+ @Test
+ fun testOnAppSelectedForNonPanelStartsFavoritingActivity() {
+ val info = ControlsServiceInfo(ComponentName("test_pkg", "service"), "", null)
+ activityRule.activity.onAppSelected(info)
+
+ verifyNoMoreInteractions(dialogFactory)
+
+ assertThat(activityRule.activity.lastStartedActivity?.component?.className)
+ .isEqualTo(ControlsFavoritingActivity::class.java.name)
+
+ assertThat(activityRule.activity.triedToFinish).isTrue()
+ }
+
+ @Test
+ fun testOnAppSelectedForPanelTriggersDialog() {
+ val label = "label"
+ val info =
+ ControlsServiceInfo(
+ ComponentName("test_pkg", "service"),
+ label,
+ ComponentName("test_pkg", "activity")
+ )
+
+ val dialog: Dialog = mock()
+ whenever(dialogFactory.createConfirmationDialog(any(), any(), any())).thenReturn(dialog)
+
+ activityRule.activity.onAppSelected(info)
+ verify(dialogFactory).createConfirmationDialog(any(), eq(label), any())
+ verify(dialog).show()
+
+ assertThat(activityRule.activity.triedToFinish).isFalse()
+ }
+
+ @Test
+ fun dialogAcceptAddsPackage() {
+ val label = "label"
+ val info =
+ ControlsServiceInfo(
+ ComponentName("test_pkg", "service"),
+ label,
+ ComponentName("test_pkg", "activity")
+ )
+
+ val dialog: Dialog = mock()
+ whenever(dialogFactory.createConfirmationDialog(any(), any(), any())).thenReturn(dialog)
+
+ activityRule.activity.onAppSelected(info)
+
+ val captor: ArgumentCaptor<Consumer<Boolean>> = argumentCaptor()
+ verify(dialogFactory).createConfirmationDialog(any(), any(), capture(captor))
+
+ captor.value.accept(true)
+
+ val setCaptor: ArgumentCaptor<Set<String>> = argumentCaptor()
+ verify(authorizedPanelsRepository).addAuthorizedPanels(capture(setCaptor))
+ assertThat(setCaptor.value).containsExactly(info.componentName.packageName)
+
+ assertThat(activityRule.activity.triedToFinish).isTrue()
+ }
+
+ @Test
+ fun dialogCancelDoesntAddPackage() {
+ val label = "label"
+ val info =
+ ControlsServiceInfo(
+ ComponentName("test_pkg", "service"),
+ label,
+ ComponentName("test_pkg", "activity")
+ )
+
+ val dialog: Dialog = mock()
+ whenever(dialogFactory.createConfirmationDialog(any(), any(), any())).thenReturn(dialog)
+
+ activityRule.activity.onAppSelected(info)
+
+ val captor: ArgumentCaptor<Consumer<Boolean>> = argumentCaptor()
+ verify(dialogFactory).createConfirmationDialog(any(), any(), capture(captor))
+
+ captor.value.accept(false)
+
+ verify(authorizedPanelsRepository, never()).addAuthorizedPanels(any())
+
+ assertThat(activityRule.activity.triedToFinish).isFalse()
+ }
+
+ class TestableControlsProviderSelectorActivity(
executor: Executor,
backExecutor: Executor,
listingController: ControlsListingController,
controlsController: ControlsController,
userTracker: UserTracker,
- uiController: ControlsUiController,
+ authorizedPanelsRepository: AuthorizedPanelsRepository,
+ dialogFactory: PanelConfirmationDialogFactory,
private val mockDispatcher: OnBackInvokedDispatcher,
private val latch: CountDownLatch
) :
@@ -129,16 +236,50 @@
listingController,
controlsController,
userTracker,
- uiController
+ authorizedPanelsRepository,
+ dialogFactory
) {
+
+ var lastStartedActivity: Intent? = null
+ var triedToFinish = false
+
override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
return mockDispatcher
}
+ override fun startActivity(intent: Intent?, options: Bundle?) {
+ lastStartedActivity = intent
+ }
+
override fun onStop() {
super.onStop()
// ensures that test runner thread does not proceed until ui thread is done
latch.countDown()
}
+
+ override fun animateExitAndFinish() {
+ // Activity should only be finished from the rule.
+ triedToFinish = true
+ }
+ }
+
+ companion object {
+ private fun ControlsServiceInfo(
+ componentName: ComponentName,
+ label: CharSequence,
+ panelComponentName: ComponentName? = null
+ ): ControlsServiceInfo {
+ val serviceInfo =
+ ServiceInfo().apply {
+ applicationInfo = ApplicationInfo()
+ packageName = componentName.packageName
+ name = componentName.className
+ }
+ return Mockito.spy(ControlsServiceInfo(mock(), serviceInfo)).apply {
+ doReturn(label).`when`(this).loadLabel()
+ doReturn(mock<Drawable>()).`when`(this).loadIcon()
+ doReturn(panelComponentName).`when`(this).panelActivity
+ }
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
new file mode 100644
index 0000000..756f267
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls.management
+
+import android.content.DialogInterface
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PanelConfirmationDialogFactoryTest : SysuiTestCase() {
+
+ @Test
+ fun testDialogHasCorrectInfo() {
+ val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+ val factory = PanelConfirmationDialogFactory { mockDialog }
+ val appName = "appName"
+
+ factory.createConfirmationDialog(context, appName) {}
+
+ verify(mockDialog).setCanceledOnTouchOutside(true)
+ verify(mockDialog)
+ .setTitle(context.getString(R.string.controls_panel_authorization_title, appName))
+ verify(mockDialog)
+ .setMessage(context.getString(R.string.controls_panel_authorization, appName))
+ }
+
+ @Test
+ fun testDialogPositiveButton() {
+ val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+ val factory = PanelConfirmationDialogFactory { mockDialog }
+
+ var response: Boolean? = null
+
+ factory.createConfirmationDialog(context, "") { response = it }
+
+ val captor: ArgumentCaptor<DialogInterface.OnClickListener> = argumentCaptor()
+ verify(mockDialog).setPositiveButton(eq(R.string.controls_dialog_ok), capture(captor))
+
+ captor.value.onClick(mockDialog, DialogInterface.BUTTON_POSITIVE)
+
+ assertThat(response).isTrue()
+ }
+
+ @Test
+ fun testDialogNeutralButton() {
+ val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+ val factory = PanelConfirmationDialogFactory { mockDialog }
+
+ var response: Boolean? = null
+
+ factory.createConfirmationDialog(context, "") { response = it }
+
+ val captor: ArgumentCaptor<DialogInterface.OnClickListener> = argumentCaptor()
+ verify(mockDialog).setNeutralButton(eq(R.string.cancel), capture(captor))
+
+ captor.value.onClick(mockDialog, DialogInterface.BUTTON_NEUTRAL)
+
+ assertThat(response).isFalse()
+ }
+
+ @Test
+ fun testDialogCancel() {
+ val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+ val factory = PanelConfirmationDialogFactory { mockDialog }
+
+ var response: Boolean? = null
+
+ factory.createConfirmationDialog(context, "") { response = it }
+
+ val captor: ArgumentCaptor<DialogInterface.OnCancelListener> = argumentCaptor()
+ verify(mockDialog).setOnCancelListener(capture(captor))
+
+ captor.value.onCancel(mockDialog)
+
+ assertThat(response).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
new file mode 100644
index 0000000..b91a3fd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls.panels
+
+import android.content.SharedPreferences
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.FakeSharedPreferences
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var userTracker: UserTracker
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_controlsPreferredPackages,
+ arrayOf<String>()
+ )
+ whenever(userTracker.userId).thenReturn(0)
+ }
+
+ @Test
+ fun testPreApprovedPackagesAreSeededIfNoSavedPreferences() {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_controlsPreferredPackages,
+ arrayOf(TEST_PACKAGE)
+ )
+ val sharedPrefs = FakeSharedPreferences()
+ val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+ val repository = createRepository(fileManager)
+
+ assertThat(repository.getAuthorizedPanels()).containsExactly(TEST_PACKAGE)
+ assertThat(sharedPrefs.getStringSet(KEY, null)).containsExactly(TEST_PACKAGE)
+ }
+
+ @Test
+ fun testPreApprovedPackagesNotSeededIfEmptySavedPreferences() {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_controlsPreferredPackages,
+ arrayOf(TEST_PACKAGE)
+ )
+ val sharedPrefs = FakeSharedPreferences()
+ sharedPrefs.edit().putStringSet(KEY, emptySet()).apply()
+ val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+ createRepository(fileManager)
+
+ assertThat(sharedPrefs.getStringSet(KEY, null)).isEmpty()
+ }
+
+ @Test
+ fun testPreApprovedPackagesOnlySetForUserThatDoesntHaveThem() {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_controlsPreferredPackages,
+ arrayOf(TEST_PACKAGE)
+ )
+ val sharedPrefs_0 = FakeSharedPreferences()
+ val sharedPrefs_1 = FakeSharedPreferences()
+ sharedPrefs_1.edit().putStringSet(KEY, emptySet()).apply()
+ val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs_0, 1 to sharedPrefs_1))
+ val repository = createRepository(fileManager)
+
+ assertThat(repository.getAuthorizedPanels()).containsExactly(TEST_PACKAGE)
+ whenever(userTracker.userId).thenReturn(1)
+ assertThat(repository.getAuthorizedPanels()).isEmpty()
+ }
+
+ @Test
+ fun testGetAuthorizedPackages() {
+ val sharedPrefs = FakeSharedPreferences()
+ sharedPrefs.edit().putStringSet(KEY, mutableSetOf(TEST_PACKAGE)).apply()
+ val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+
+ val repository = createRepository(fileManager)
+ assertThat(repository.getAuthorizedPanels()).containsExactly(TEST_PACKAGE)
+ }
+
+ @Test
+ fun testSetAuthorizedPackage() {
+ val sharedPrefs = FakeSharedPreferences()
+ val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+
+ val repository = createRepository(fileManager)
+ repository.addAuthorizedPanels(setOf(TEST_PACKAGE))
+ assertThat(sharedPrefs.getStringSet(KEY, null)).containsExactly(TEST_PACKAGE)
+ }
+
+ private fun createRepository(userFileManager: UserFileManager): AuthorizedPanelsRepositoryImpl {
+ return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker)
+ }
+
+ private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
+ UserFileManager {
+ override fun getFile(fileName: String, userId: Int): File {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSharedPreferences(
+ fileName: String,
+ mode: Int,
+ userId: Int
+ ): SharedPreferences {
+ if (fileName != FILE_NAME) {
+ throw IllegalArgumentException("Preference files must be $FILE_NAME")
+ }
+ return sharedPrefs.getValue(userId)
+ }
+ }
+
+ companion object {
+ private const val FILE_NAME = "controls_prefs"
+ private const val KEY = "authorized_panels"
+ private const val TEST_PACKAGE = "package"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index edc6882..85f9961 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -20,6 +20,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.os.UserHandle
import android.service.controls.ControlsProviderService
@@ -39,8 +40,10 @@
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.settings.FakeControlsSettingsRepository
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
@@ -91,6 +94,9 @@
@Mock lateinit var userTracker: UserTracker
@Mock lateinit var taskViewFactory: TaskViewFactory
@Mock lateinit var dumpManager: DumpManager
+ @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
+ @Mock lateinit var featureFlags: FeatureFlags
+ @Mock lateinit var packageManager: PackageManager
val sharedPreferences = FakeSharedPreferences()
lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
@@ -120,6 +126,7 @@
ControlsUiControllerImpl(
Lazy { controlsController },
context,
+ packageManager,
uiExecutor,
bgExecutor,
Lazy { controlsListingController },
@@ -132,6 +139,8 @@
userTracker,
Optional.of(taskViewFactory),
controlsSettingsRepository,
+ authorizedPanelsRepository,
+ featureFlags,
dumpManager
)
`when`(
@@ -240,7 +249,9 @@
@Test
fun testPanelCallsTaskViewFactoryCreate() {
mockLayoutInflater()
- val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+ val packageName = "pkg"
+ `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
+ val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
val serviceInfo = setUpPanel(panel)
underTest.show(parent, {}, context)
@@ -258,9 +269,11 @@
@Test
fun testPanelControllerStartActivityWithCorrectArguments() {
mockLayoutInflater()
+ val packageName = "pkg"
+ `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
- val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+ val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
val serviceInfo = setUpPanel(panel)
underTest.show(parent, {}, context)
@@ -290,9 +303,11 @@
@Test
fun testPendingIntentExtrasAreModified() {
mockLayoutInflater()
+ val packageName = "pkg"
+ `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
- val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+ val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
val serviceInfo = setUpPanel(panel)
underTest.show(parent, {}, context)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
new file mode 100644
index 0000000..dbaf94f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.controls.ui
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class OverflowMenuAdapterTest : SysuiTestCase() {
+
+ @Test
+ fun testGetItemId() {
+ val ids = listOf(27L, 73L)
+ val labels = listOf("first", "second")
+ val adapter =
+ OverflowMenuAdapter(
+ context,
+ layoutId = 0,
+ labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) }
+ ) { true }
+
+ ids.forEachIndexed { index, id -> assertThat(adapter.getItemId(index)).isEqualTo(id) }
+ }
+
+ @Test
+ fun testCheckEnabled() {
+ val ids = listOf(27L, 73L)
+ val labels = listOf("first", "second")
+ val adapter =
+ OverflowMenuAdapter(
+ context,
+ layoutId = 0,
+ labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) }
+ ) { position -> position == 0 }
+
+ assertThat(adapter.isEnabled(0)).isTrue()
+ assertThat(adapter.isEnabled(1)).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index ff883eb..6b095ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -36,11 +36,10 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
+import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.BlurUtils;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import org.junit.Before;
import org.junit.Test;
@@ -81,12 +80,6 @@
BlurUtils mBlurUtils;
@Mock
- StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-
- @Mock
- KeyguardBouncer mBouncer;
-
- @Mock
ViewRootImpl mViewRoot;
@Mock
@@ -96,6 +89,9 @@
DreamOverlayAnimationsController mAnimationsController;
@Mock
+ BouncerlessScrimController mBouncerlessScrimController;
+
+ @Mock
DreamOverlayStateController mStateController;
DreamOverlayContainerViewController mController;
@@ -106,7 +102,6 @@
when(mDreamOverlayContainerView.getResources()).thenReturn(mResources);
when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
- when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(mBouncer);
when(mDreamOverlayContainerView.getViewRootImpl()).thenReturn(mViewRoot);
mController = new DreamOverlayContainerViewController(
@@ -114,7 +109,6 @@
mComplicationHostViewController,
mDreamOverlayContentView,
mDreamOverlayStatusBarViewController,
- mStatusBarKeyguardViewManager,
mBlurUtils,
mHandler,
mResources,
@@ -123,7 +117,8 @@
MILLIS_UNTIL_FULL_JITTER,
mPrimaryBouncerCallbackInteractor,
mAnimationsController,
- mStateController);
+ mStateController,
+ mBouncerlessScrimController);
}
@Test
@@ -170,7 +165,8 @@
final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
mController.onViewAttached();
- verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
+ verify(mPrimaryBouncerCallbackInteractor).addBouncerExpansionCallback(
+ bouncerExpansionCaptor.capture());
bouncerExpansionCaptor.getValue().onExpansionChanged(0.5f);
verify(mBlurUtils, never()).applyBlur(eq(mViewRoot), anyInt(), eq(false));
@@ -181,7 +177,8 @@
final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
mController.onViewAttached();
- verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
+ verify(mPrimaryBouncerCallbackInteractor).addBouncerExpansionCallback(
+ bouncerExpansionCaptor.capture());
final float blurRadius = 1337f;
when(mBlurUtils.blurRadiusOfRatio(anyFloat())).thenReturn(blurRadius);
@@ -217,6 +214,27 @@
}
@Test
+ public void testSkipEntryAnimationsWhenExitingLowLight() {
+ ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+ when(mStateController.isLowLightActive()).thenReturn(false);
+
+ // Call onInit so that the callback is added.
+ mController.onInit();
+ verify(mStateController).addCallback(callbackCaptor.capture());
+
+ // Send the signal that low light is exiting
+ callbackCaptor.getValue().onExitLowLight();
+
+ // View is attached to trigger animations.
+ mController.onViewAttached();
+
+ // Entry animations should be started then immediately ended to skip to the end.
+ verify(mAnimationsController).startEntryAnimations();
+ verify(mAnimationsController).endAnimations();
+ }
+
+ @Test
public void testCancelDreamEntryAnimationsOnDetached() {
mController.onViewAttached();
mController.onViewDetached();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index ee989d1..b7d0f29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -251,6 +251,30 @@
}
@Test
+ public void testNotifyLowLightExit() {
+ final DreamOverlayStateController stateController =
+ new DreamOverlayStateController(mExecutor, true);
+
+ stateController.addCallback(mCallback);
+ mExecutor.runAllReady();
+ assertThat(stateController.isLowLightActive()).isFalse();
+
+ // Turn low light on then off to trigger the exiting callback.
+ stateController.setLowLightActive(true);
+ stateController.setLowLightActive(false);
+
+ // Callback was only called once, when
+ mExecutor.runAllReady();
+ verify(mCallback, times(1)).onExitLowLight();
+ assertThat(stateController.isLowLightActive()).isFalse();
+
+ // Set with false again, which should not cause the callback to trigger again.
+ stateController.setLowLightActive(false);
+ mExecutor.runAllReady();
+ verify(mCallback, times(1)).onExitLowLight();
+ }
+
+ @Test
public void testNotifyEntryAnimationsFinishedChanged() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
new file mode 100644
index 0000000..19347c7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.conditions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.condition.Condition;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamConditionTest extends SysuiTestCase {
+ @Mock
+ Context mContext;
+
+ @Mock
+ Condition.Callback mCallback;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensure a dreaming state immediately triggers the condition.
+ */
+ @Test
+ public void testInitialState() {
+ final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
+ when(mContext.registerReceiver(any(), any())).thenReturn(intent);
+ final DreamCondition condition = new DreamCondition(mContext);
+ condition.addCallback(mCallback);
+ condition.start();
+
+ verify(mCallback).onConditionChanged(eq(condition));
+ assertThat(condition.isConditionMet()).isTrue();
+ }
+
+ /**
+ * Ensure that changing dream state triggers condition.
+ */
+ @Test
+ public void testChange() {
+ final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ when(mContext.registerReceiver(receiverCaptor.capture(), any())).thenReturn(intent);
+ final DreamCondition condition = new DreamCondition(mContext);
+ condition.addCallback(mCallback);
+ condition.start();
+ clearInvocations(mCallback);
+ receiverCaptor.getValue().onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
+ verify(mCallback).onConditionChanged(eq(condition));
+ assertThat(condition.isConditionMet()).isFalse();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index f64179d..3a168d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -41,12 +41,13 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.touch.scrim.ScrimController;
+import com.android.systemui.dreams.touch.scrim.ScrimManager;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
import org.junit.Before;
@@ -63,10 +64,13 @@
@RunWith(AndroidTestingRunner.class)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@Mock
- StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ CentralSurfaces mCentralSurfaces;
@Mock
- CentralSurfaces mCentralSurfaces;
+ ScrimManager mScrimManager;
+
+ @Mock
+ ScrimController mScrimController;
@Mock
NotificationShadeWindowController mNotificationShadeWindowController;
@@ -111,7 +115,7 @@
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
mDisplayMetrics,
- mStatusBarKeyguardViewManager,
+ mScrimManager,
Optional.of(mCentralSurfaces),
mNotificationShadeWindowController,
mValueAnimatorCreator,
@@ -121,6 +125,7 @@
TOUCH_REGION,
mUiEventLogger);
+ when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
when(mCentralSurfaces.getDisplayHeight()).thenReturn((float) SCREEN_HEIGHT_PX);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -193,7 +198,7 @@
assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
.isTrue();
- verify(mStatusBarKeyguardViewManager, never()).onPanelExpansionChanged(any());
+ verify(mScrimController, never()).expand(any());
}
/**
@@ -220,7 +225,7 @@
assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
.isTrue();
- verify(mStatusBarKeyguardViewManager, never()).onPanelExpansionChanged(any());
+ verify(mScrimController, never()).expand(any());
}
/**
@@ -274,12 +279,12 @@
final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
0, direction == Direction.UP ? SCREEN_HEIGHT_PX - distanceY : distanceY, 0);
- reset(mStatusBarKeyguardViewManager);
+ reset(mScrimController);
assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
.isTrue();
// Ensure only called once
- verify(mStatusBarKeyguardViewManager).onPanelExpansionChanged(any());
+ verify(mScrimController).expand(any());
final float expansion = isBouncerInitiallyShowing ? percent : 1 - percent;
final float dragDownAmount = event2.getY() - event1.getY();
@@ -288,7 +293,7 @@
ShadeExpansionChangeEvent event =
new ShadeExpansionChangeEvent(
expansion, /* expanded= */ false, /* tracking= */ true, dragDownAmount);
- verify(mStatusBarKeyguardViewManager).onPanelExpansionChanged(event);
+ verify(mScrimController).expand(event);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index a807407..178b9cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -424,6 +424,32 @@
verify(gestureListener2).onDown(eq(followupEvent));
}
+ @Test
+ public void testOnRemovedCallbackOnStopMonitoring() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final DreamTouchHandler.TouchSession.Callback callback =
+ Mockito.mock(DreamTouchHandler.TouchSession.Callback.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ session.registerCallback(callback);
+
+ environment.executeAll();
+
+ environment.updateLifecycle(observerOwnerPair -> {
+ observerOwnerPair.first.onPause(observerOwnerPair.second);
+ });
+
+ environment.executeAll();
+
+ verify(callback).onRemoved();
+ }
+
public GestureDetector.OnGestureListener registerGestureListener(DreamTouchHandler handler) {
final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
GestureDetector.OnGestureListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
new file mode 100644
index 0000000..79c535a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.touch.scrim;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.PowerManager;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BouncerlessScrimControllerTest extends SysuiTestCase {
+ @Mock
+ BouncerlessScrimController.Callback mCallback;
+
+ @Mock
+ PowerManager mPowerManager;
+
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testWakeupOnSwipeOpen() {
+ final BouncerlessScrimController scrimController =
+ new BouncerlessScrimController(mExecutor, mPowerManager);
+ scrimController.addCallback(mCallback);
+ scrimController.expand(new ShadeExpansionChangeEvent(.5f, true, false, 0.0f));
+ mExecutor.runAllReady();
+ verify(mPowerManager).wakeUp(anyLong(), eq(PowerManager.WAKE_REASON_GESTURE), any());
+ verify(mCallback).onWakeup();
+ }
+
+ @Test
+ public void testExpansionPropagation() {
+ final BouncerlessScrimController scrimController =
+ new BouncerlessScrimController(mExecutor, mPowerManager);
+ scrimController.addCallback(mCallback);
+ final ShadeExpansionChangeEvent expansionEvent =
+ new ShadeExpansionChangeEvent(0.5f, false, false, 0.0f);
+ scrimController.expand(expansionEvent);
+ mExecutor.runAllReady();
+ verify(mCallback).onExpansion(eq(expansionEvent));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
new file mode 100644
index 0000000..ac9822d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dreams.touch.scrim;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScrimManagerTest extends SysuiTestCase {
+ @Mock
+ ScrimController mBouncerlessScrimController;
+
+ @Mock
+ ScrimController mBouncerScrimController;
+
+ @Mock
+ KeyguardStateController mKeyguardStateController;
+
+ @Mock
+ ScrimManager.Callback mCallback;
+
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testControllerSelection() {
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+ ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+ final ScrimManager manager = new ScrimManager(mExecutor, mBouncerScrimController,
+ mBouncerlessScrimController, mKeyguardStateController);
+ verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
+
+ assertThat(manager.getCurrentController()).isEqualTo(mBouncerScrimController);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+ callbackCaptor.getValue().onKeyguardShowingChanged();
+ mExecutor.runAllReady();
+ assertThat(manager.getCurrentController()).isEqualTo(mBouncerlessScrimController);
+ }
+
+ @Test
+ public void testCallback() {
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+ ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+ final ScrimManager manager = new ScrimManager(mExecutor, mBouncerScrimController,
+ mBouncerlessScrimController, mKeyguardStateController);
+ verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
+
+ manager.addCallback(mCallback);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+ callbackCaptor.getValue().onKeyguardShowingChanged();
+ mExecutor.runAllReady();
+ verify(mCallback).onScrimControllerChanged(eq(mBouncerlessScrimController));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index c0af0cb..fb54d6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -62,6 +62,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -184,6 +185,7 @@
mainDispatcher = testDispatcher,
backgroundHandler = backgroundHandler,
)
+ underTest.mainDispatcher = UnconfinedTestDispatcher()
underTest.attachInfoForTesting(
context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index 8da4eae..58cdec4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -19,6 +19,7 @@
import android.app.StatusBarManager
import android.content.Context
+import android.content.pm.PackageManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.camera.CameraGestureHelper
@@ -32,7 +33,6 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
-import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -43,16 +43,19 @@
@Mock private lateinit var cameraGestureHelper: CameraGestureHelper
@Mock private lateinit var context: Context
+ @Mock private lateinit var packageManager: PackageManager
private lateinit var underTest: CameraQuickAffordanceConfig
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ setLaunchable(true)
underTest =
CameraQuickAffordanceConfig(
context,
+ packageManager,
) {
cameraGestureHelper
}
@@ -86,6 +89,7 @@
}
private fun setLaunchable(isLaunchable: Boolean) {
- whenever(cameraGestureHelper.canCameraGestureBeLaunched(anyInt())).thenReturn(isLaunchable)
+ whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
+ .thenReturn(isLaunchable)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index c4ae2db..9203f05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -55,7 +55,11 @@
MockitoAnnotations.initMocks(this)
testScope = TestScope()
- underTest = DeviceEntryFingerprintAuthRepositoryImpl(keyguardUpdateMonitor)
+ underTest =
+ DeviceEntryFingerprintAuthRepositoryImpl(
+ keyguardUpdateMonitor,
+ testScope.backgroundScope,
+ )
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
index 1da7241..68fff26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeBiometricRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.util.time.FakeSystemClock
@@ -46,6 +47,8 @@
private lateinit var underTest: AlternateBouncerInteractor
private lateinit var bouncerRepository: KeyguardBouncerRepository
private lateinit var biometricRepository: FakeBiometricRepository
+ private lateinit var deviceEntryFingerprintAuthRepository:
+ FakeDeviceEntryFingerprintAuthRepository
@Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var bouncerLogger: TableLogBuffer
@@ -62,11 +65,13 @@
bouncerLogger,
)
biometricRepository = FakeBiometricRepository()
+ deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) }
underTest =
AlternateBouncerInteractor(
bouncerRepository,
biometricRepository,
+ deviceEntryFingerprintAuthRepository,
systemClock,
keyguardUpdateMonitor,
featureFlags,
@@ -112,6 +117,14 @@
}
@Test
+ fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
+ givenCanShowAlternateBouncer()
+ deviceEntryFingerprintAuthRepository.setLockedOut(true)
+
+ assertFalse(underTest.canShowAlternateBouncerForFingerprint())
+ }
+
+ @Test
fun show_whenCanShow() {
givenCanShowAlternateBouncer()
@@ -148,6 +161,7 @@
biometricRepository.setFingerprintEnrolled(true)
biometricRepository.setStrongBiometricAllowed(true)
biometricRepository.setFingerprintEnabledByDevicePolicy(true)
+ deviceEntryFingerprintAuthRepository.setLockedOut(false)
}
private fun givenCannotShowAlternateBouncer() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
new file mode 100644
index 0000000..9d60b16
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.domain.interactor
+
+import android.content.Intent
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardLongPressInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var logger: UiEventLogger
+
+ private lateinit var underTest: KeyguardLongPressInteractor
+
+ private lateinit var testScope: TestScope
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ runBlocking { createUnderTest() }
+ }
+
+ @Test
+ fun isEnabled() =
+ testScope.runTest {
+ val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
+ KeyguardState.values().forEach { keyguardState ->
+ setUpState(
+ keyguardState = keyguardState,
+ )
+
+ if (keyguardState == KeyguardState.LOCKSCREEN) {
+ assertThat(isEnabled()).isTrue()
+ } else {
+ assertThat(isEnabled()).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun `isEnabled - always false when quick settings are visible`() =
+ testScope.runTest {
+ val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
+ KeyguardState.values().forEach { keyguardState ->
+ setUpState(
+ keyguardState = keyguardState,
+ isQuickSettingsVisible = true,
+ )
+
+ assertThat(isEnabled()).isFalse()
+ }
+ }
+
+ @Test
+ fun `long-pressed - pop-up clicked - starts activity`() =
+ testScope.runTest {
+ val menu = collectLastValue(underTest.menu)
+ runCurrent()
+
+ val x = 100
+ val y = 123
+ underTest.onLongPress(x, y)
+ assertThat(menu()).isNotNull()
+ assertThat(menu()?.position?.x).isEqualTo(x)
+ assertThat(menu()?.position?.y).isEqualTo(y)
+
+ menu()?.onClicked?.invoke()
+
+ assertThat(menu()).isNull()
+ verify(activityStarter).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+ }
+
+ @Test
+ fun `long-pressed - pop-up dismissed - never starts activity`() =
+ testScope.runTest {
+ val menu = collectLastValue(underTest.menu)
+ runCurrent()
+
+ menu()?.onDismissed?.invoke()
+
+ assertThat(menu()).isNull()
+ verify(activityStarter, never()).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+ }
+
+ @Suppress("DEPRECATION") // We're okay using ACTION_CLOSE_SYSTEM_DIALOGS on system UI.
+ @Test
+ fun `long pressed - close dialogs broadcast received - popup dismissed`() =
+ testScope.runTest {
+ val menu = collectLastValue(underTest.menu)
+ runCurrent()
+
+ underTest.onLongPress(123, 456)
+ assertThat(menu()).isNotNull()
+
+ fakeBroadcastDispatcher.registeredReceivers.forEach { broadcastReceiver ->
+ broadcastReceiver.onReceive(context, Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
+ }
+
+ assertThat(menu()).isNull()
+ }
+
+ @Test
+ fun `logs when menu is shown`() =
+ testScope.runTest {
+ collectLastValue(underTest.menu)
+ runCurrent()
+
+ underTest.onLongPress(100, 123)
+
+ verify(logger)
+ .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
+ }
+
+ @Test
+ fun `logs when menu is clicked`() =
+ testScope.runTest {
+ val menu = collectLastValue(underTest.menu)
+ runCurrent()
+
+ underTest.onLongPress(100, 123)
+ menu()?.onClicked?.invoke()
+
+ verify(logger)
+ .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
+ }
+
+ private suspend fun createUnderTest(
+ isLongPressFeatureEnabled: Boolean = true,
+ isRevampedWppFeatureEnabled: Boolean = true,
+ ) {
+ testScope = TestScope()
+ keyguardRepository = FakeKeyguardRepository()
+ keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+
+ underTest =
+ KeyguardLongPressInteractor(
+ unsafeContext = context,
+ scope = testScope.backgroundScope,
+ transitionInteractor =
+ KeyguardTransitionInteractor(
+ repository = keyguardTransitionRepository,
+ ),
+ repository = keyguardRepository,
+ activityStarter = activityStarter,
+ logger = logger,
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled)
+ set(Flags.REVAMPED_WALLPAPER_UI, isRevampedWppFeatureEnabled)
+ },
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ )
+ setUpState()
+ }
+
+ private suspend fun setUpState(
+ keyguardState: KeyguardState = KeyguardState.LOCKSCREEN,
+ isQuickSettingsVisible: Boolean = false,
+ ) {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = keyguardState,
+ ),
+ )
+ keyguardRepository.setQuickSettingsVisible(isVisible = isQuickSettingsVisible)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 5a7a3d4..3a871b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -519,6 +519,43 @@
}
@Test
+ fun `GONE to LOCKSREEN`() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to GONE
+ runner.startTransition(
+ testScope,
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ animator =
+ ValueAnimator().apply {
+ duration = 10
+ interpolator = Interpolators.LINEAR
+ },
+ )
+ )
+ runCurrent()
+ reset(mockTransitionRepository)
+
+ // WHEN the keyguard starts to show
+ keyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(mockTransitionRepository).startTransition(capture())
+ }
+ // THEN a transition to AOD should occur
+ assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.GONE)
+ assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun `GONE to DREAMING`() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 022afdd..4b04b7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -234,7 +234,10 @@
@Test
fun `startButton - in preview mode - visible even when keyguard not showing`() =
testScope.runTest {
- underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
+ underTest.enablePreviewMode(
+ initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ shouldHighlightSelectedAffordance = true,
+ )
repository.setKeyguardShowing(false)
val latest = collectLastValue(underTest.startButton)
@@ -263,6 +266,7 @@
icon = icon,
canShowWhileLocked = false,
intent = Intent("action"),
+ isSelected = true,
),
configKey = configKey,
)
@@ -270,6 +274,60 @@
}
@Test
+ fun `endButton - in higlighted preview mode - dimmed when other is selected`() =
+ testScope.runTest {
+ underTest.enablePreviewMode(
+ initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ shouldHighlightSelectedAffordance = true,
+ )
+ repository.setKeyguardShowing(false)
+ val startButton = collectLastValue(underTest.startButton)
+ val endButton = collectLastValue(underTest.endButton)
+
+ val icon: Icon = mock()
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = true,
+ isActivated = true,
+ icon = icon,
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ ),
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_END,
+ testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = true,
+ isActivated = true,
+ icon = icon,
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ ),
+ )
+
+ assertQuickAffordanceViewModel(
+ viewModel = endButton(),
+ testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = false,
+ isActivated = true,
+ icon = icon,
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ isDimmed = true,
+ ),
+ configKey = configKey,
+ )
+ }
+
+ @Test
fun `endButton - present - visible model - do nothing on click`() =
testScope.runTest {
repository.setKeyguardShowing(true)
@@ -377,7 +435,10 @@
@Test
fun `alpha - in preview mode - does not change`() =
testScope.runTest {
- underTest.enablePreviewMode(null)
+ underTest.enablePreviewMode(
+ initiallySelectedSlotId = null,
+ shouldHighlightSelectedAffordance = false,
+ )
val value = collectLastValue(underTest.alpha)
assertThat(value()).isEqualTo(1f)
@@ -639,6 +700,8 @@
assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
assertThat(viewModel.isClickable).isEqualTo(testConfig.isClickable)
assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
+ assertThat(viewModel.isSelected).isEqualTo(testConfig.isSelected)
+ assertThat(viewModel.isDimmed).isEqualTo(testConfig.isDimmed)
if (testConfig.isVisible) {
assertThat(viewModel.icon).isEqualTo(testConfig.icon)
viewModel.onClicked.invoke(
@@ -664,6 +727,8 @@
val icon: Icon? = null,
val canShowWhileLocked: Boolean = false,
val intent: Intent? = null,
+ val isSelected: Boolean = false,
+ val isDimmed: Boolean = false,
) {
init {
check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
index c88f84a..54fc493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
@@ -71,7 +71,7 @@
waitUntilComplete(info.animator!!)
}
- suspend private fun waitUntilComplete(animator: ValueAnimator) {
+ private suspend fun waitUntilComplete(animator: ValueAnimator) {
withContext(Dispatchers.Main) {
val startTime = System.currentTimeMillis()
while (!isTerminated && animator.isRunning()) {
@@ -96,6 +96,6 @@
override fun setFrameDelay(delay: Long) {}
companion object {
- private const val MAX_TEST_DURATION = 100L
+ private const val MAX_TEST_DURATION = 200L
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 1687fdc..1ac6695 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -134,7 +134,8 @@
private val clock = FakeSystemClock()
@Mock private lateinit var tunerService: TunerService
@Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable>
- @Captor lateinit var callbackCaptor: ArgumentCaptor<(String, PlaybackState) -> Unit>
+ @Captor lateinit var stateCallbackCaptor: ArgumentCaptor<(String, PlaybackState) -> Unit>
+ @Captor lateinit var sessionCallbackCaptor: ArgumentCaptor<(String) -> Unit>
@Captor lateinit var smartSpaceConfigBuilderCaptor: ArgumentCaptor<SmartspaceConfig>
private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
@@ -184,6 +185,8 @@
)
verify(tunerService)
.addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
+ verify(mediaTimeoutListener).stateCallback = capture(stateCallbackCaptor)
+ verify(mediaTimeoutListener).sessionCallback = capture(sessionCallbackCaptor)
session = MediaSession(context, "MediaDataManagerTestSession")
mediaNotification =
SbnBuilder().run {
@@ -230,6 +233,7 @@
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
whenever(mediaFlags.isExplicitIndicatorEnabled()).thenReturn(true)
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false)
whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
}
@@ -547,6 +551,7 @@
mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+
verify(listener)
.onMediaDataLoaded(
eq(KEY),
@@ -558,9 +563,21 @@
)
val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
- val resumableData = data.copy(resumeAction = Runnable {})
- mediaDataManager.onMediaDataLoaded(KEY, null, resumableData)
- mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData)
+
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY_2),
+ eq(null),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ val data2 = mediaDataCaptor.value
+ assertThat(data2.resumption).isFalse()
+
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
+ mediaDataManager.onMediaDataLoaded(KEY_2, null, data2.copy(resumeAction = Runnable {}))
reset(listener)
// WHEN the first is removed
mediaDataManager.onNotificationRemoved(KEY)
@@ -1310,11 +1327,10 @@
fun testPlaybackStateChange_keyExists_callsListener() {
// Notification has been added
addNotificationAndLoad()
- verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
// Callback gets an updated state
val state = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
- callbackCaptor.value.invoke(KEY, state)
+ stateCallbackCaptor.value.invoke(KEY, state)
// Listener is notified of updated state
verify(listener)
@@ -1332,11 +1348,10 @@
@Test
fun testPlaybackStateChange_keyDoesNotExist_doesNothing() {
val state = PlaybackState.Builder().build()
- verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
// No media added with this key
- callbackCaptor.value.invoke(KEY, state)
+ stateCallbackCaptor.value.invoke(KEY, state)
verify(listener, never())
.onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
@@ -1352,10 +1367,9 @@
// And then get a state update
val state = PlaybackState.Builder().build()
- verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
// Then no changes are made
- callbackCaptor.value.invoke(KEY, state)
+ stateCallbackCaptor.value.invoke(KEY, state)
verify(listener, never())
.onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
@@ -1367,8 +1381,7 @@
whenever(controller.playbackState).thenReturn(state)
addNotificationAndLoad()
- verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
- callbackCaptor.value.invoke(KEY, state)
+ stateCallbackCaptor.value.invoke(KEY, state)
verify(listener)
.onMediaDataLoaded(
@@ -1410,8 +1423,7 @@
backgroundExecutor.runAllReady()
foregroundExecutor.runAllReady()
- verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
- callbackCaptor.value.invoke(PACKAGE_NAME, state)
+ stateCallbackCaptor.value.invoke(PACKAGE_NAME, state)
verify(listener)
.onMediaDataLoaded(
@@ -1436,8 +1448,7 @@
.build()
addNotificationAndLoad()
- verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
- callbackCaptor.value.invoke(KEY, state)
+ stateCallbackCaptor.value.invoke(KEY, state)
verify(listener)
.onMediaDataLoaded(
@@ -1485,6 +1496,177 @@
assertThat(mediaDataCaptor.value.isClearable).isFalse()
}
+ @Test
+ fun testRetain_notifPlayer_notifRemoved_setToResume() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+ // When a media control based on notification is added, times out, and then removed
+ addNotificationAndLoad()
+ mediaDataManager.setTimedOut(KEY, timedOut = true)
+ assertThat(mediaDataCaptor.value.active).isFalse()
+ mediaDataManager.onNotificationRemoved(KEY)
+
+ // It is converted to a resume player
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.active).isFalse()
+ verify(logger)
+ .logActiveConvertedToResume(
+ anyInt(),
+ eq(PACKAGE_NAME),
+ eq(mediaDataCaptor.value.instanceId)
+ )
+ }
+
+ @Test
+ fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+ // When a media control based on notification is added and times out
+ addNotificationAndLoad()
+ mediaDataManager.setTimedOut(KEY, timedOut = true)
+ assertThat(mediaDataCaptor.value.active).isFalse()
+
+ // and then the session is destroyed
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It remains as a regular player
+ verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never())
+ .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+ // When a media control based on notification is added and then removed, without timing out
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ mediaDataManager.onNotificationRemoved(KEY)
+
+ // It is fully removed
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+ verify(listener, never())
+ .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ fun testRetain_canResume_removeWhileActive_setToResume() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+ // When a media control that supports resumption is added
+ addNotificationAndLoad()
+ val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {})
+ mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable)
+
+ // And then removed while still active
+ mediaDataManager.onNotificationRemoved(KEY)
+
+ // It is converted to a resume player
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.active).isFalse()
+ verify(logger)
+ .logActiveConvertedToResume(
+ anyInt(),
+ eq(PACKAGE_NAME),
+ eq(mediaDataCaptor.value.instanceId)
+ )
+ }
+
+ @Test
+ fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control with PlaybackState actions is added, times out,
+ // and then the notification is removed
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ mediaDataManager.setTimedOut(KEY, timedOut = true)
+ mediaDataManager.onNotificationRemoved(KEY)
+
+ // It remains as a regular player
+ verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never())
+ .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control with PlaybackState actions is added, times out,
+ // and then the session is destroyed
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ mediaDataManager.setTimedOut(KEY, timedOut = true)
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is converted to a resume player
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.active).isFalse()
+ verify(logger)
+ .logActiveConvertedToResume(
+ anyInt(),
+ eq(PACKAGE_NAME),
+ eq(mediaDataCaptor.value.instanceId)
+ )
+ }
+
+ @Test
+ fun testRetain_sessionPlayer_destroyedWhileActive_fullyRemoved() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control using session actions is added, and then the session is destroyed
+ // without timing out first
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is fully removed
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+ verify(listener, never())
+ .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ }
+
/** Helper function to add a media notification and capture the resulting MediaData */
private fun addNotificationAndLoad() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
@@ -1500,4 +1682,12 @@
eq(false)
)
}
+
+ /** Helper function to set up a PlaybackState with action */
+ private fun addPlaybackStateAction() {
+ val stateActions = PlaybackState.ACTION_PLAY_PAUSE
+ val stateBuilder = PlaybackState.Builder().setActions(stateActions)
+ stateBuilder.setState(PlaybackState.STATE_PAUSED, 0, 1.0f)
+ whenever(controller.playbackState).thenReturn(stateBuilder.build())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
index 344dffa..92bf84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
@@ -72,6 +72,7 @@
private lateinit var executor: FakeExecutor
@Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
@Mock private lateinit var stateCallback: (String, PlaybackState) -> Unit
+ @Mock private lateinit var sessionCallback: (String) -> Unit
@Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback>
@Captor
private lateinit var dozingCallbackCaptor:
@@ -99,6 +100,7 @@
)
mediaTimeoutListener.timeoutCallback = timeoutCallback
mediaTimeoutListener.stateCallback = stateCallback
+ mediaTimeoutListener.sessionCallback = sessionCallback
// Create a media session and notification for testing.
metadataBuilder =
@@ -284,6 +286,7 @@
verify(mediaController).unregisterCallback(anyObject())
assertThat(executor.numPending()).isEqualTo(0)
verify(logger).logSessionDestroyed(eq(KEY))
+ verify(sessionCallback).invoke(eq(KEY))
}
@Test
@@ -322,6 +325,7 @@
// THEN the controller is unregistered, but the timeout is still scheduled
verify(mediaController).unregisterCallback(anyObject())
assertThat(executor.numPending()).isEqualTo(1)
+ verify(sessionCallback, never()).invoke(eq(KEY))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 094d69a..9a0bd9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -243,6 +243,13 @@
}
@Test
+ public void dismissDialog_closesDialogByBroadcastSender() {
+ mMediaOutputBaseDialogImpl.dismissDialog();
+
+ verify(mBroadcastSender).closeSystemDialogs();
+ }
+
+ @Test
public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index f5432e2..117751c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -252,6 +252,13 @@
}
@Test
+ public void tryToLaunchMediaApplication_nullIntent_skip() {
+ mMediaOutputController.tryToLaunchMediaApplication();
+
+ verify(mCb, never()).dismissDialog();
+ }
+
+ @Test
public void onDevicesUpdated_unregistersNearbyDevicesCallback() throws RemoteException {
mMediaOutputController.start(mCb);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
similarity index 70%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
index 0e7bf8d..8da1c64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
@@ -22,7 +22,6 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -31,16 +30,15 @@
import org.mockito.Mockito.mock
@SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttLoggerUtilsTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
- private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
@Before
- fun setUp () {
- buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+ fun setUp() {
+ buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
}
@Test
@@ -49,35 +47,33 @@
val id = "test id"
val packageName = "this.is.a.package"
- logger.logStateChange(stateName, id, packageName)
+ MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, id, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(DEVICE_TYPE_TAG)
+ assertThat(actualString).contains(TAG)
assertThat(actualString).contains(stateName)
assertThat(actualString).contains(id)
assertThat(actualString).contains(packageName)
}
@Test
- fun logPackageNotFound_bufferHasPackageName() {
- val packageName = "this.is.a.package"
-
- logger.logPackageNotFound(packageName)
+ fun logStateChangeError_hasState() {
+ MediaTttLoggerUtils.logStateChangeError(buffer, TAG, 3456)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(packageName)
+ assertThat(actualString).contains(TAG)
+ assertThat(actualString).contains("3456")
}
@Test
- fun logRemovalBypass_bufferHasReasons() {
- val removalReason = "fakeRemovalReason"
- val bypassReason = "fakeBypassReason"
+ fun logPackageNotFound_bufferHasPackageName() {
+ val packageName = "this.is.a.package"
- logger.logRemovalBypass(removalReason, bypassReason)
+ MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(removalReason)
- assertThat(actualString).contains(bypassReason)
+ assertThat(actualString).contains(TAG)
+ assertThat(actualString).contains(packageName)
}
private fun getStringFromBuffer(): String {
@@ -87,4 +83,4 @@
}
}
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
+private const val TAG = "TEST TAG"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 561867f..8055b98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -25,7 +25,6 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -41,7 +40,6 @@
private lateinit var appIconFromPackageName: Drawable
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var applicationInfo: ApplicationInfo
- @Mock private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
@Before
fun setUp() {
@@ -67,8 +65,7 @@
@Test
fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
- val iconInfo =
- MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null, logger)
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null) {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -77,8 +74,19 @@
}
@Test
+ fun getIconInfoFromPackageName_nullPackageName_exceptionFnNotTriggered() {
+ var exceptionTriggered = false
+
+ MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null) {
+ exceptionTriggered = true
+ }
+
+ assertThat(exceptionTriggered).isFalse()
+ }
+
+ @Test
fun getIconInfoFromPackageName_invalidPackageName_returnsDefault() {
- val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName", logger)
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName") {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -87,8 +95,19 @@
}
@Test
+ fun getIconInfoFromPackageName_invalidPackageName_exceptionFnTriggered() {
+ var exceptionTriggered = false
+
+ MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = "fakePackageName") {
+ exceptionTriggered = true
+ }
+
+ assertThat(exceptionTriggered).isTrue()
+ }
+
+ @Test
fun getIconInfoFromPackageName_validPackageName_returnsAppInfo() {
- val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, logger)
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME) {}
assertThat(iconInfo.isAppIcon).isTrue()
assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName))
@@ -96,6 +115,17 @@
}
@Test
+ fun getIconInfoFromPackageName_validPackageName_exceptionFnNotTriggered() {
+ var exceptionTriggered = false
+
+ MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME) {
+ exceptionTriggered = true
+ }
+
+ assertThat(exceptionTriggered).isFalse()
+ }
+
+ @Test
fun iconInfo_toTintedIcon_loaded() {
val contentDescription = ContentDescription.Loaded("test")
val drawable = context.getDrawable(R.drawable.ic_cake)!!
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index b3e621e..bd042c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -24,7 +24,6 @@
import android.view.accessibility.AccessibilityManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -35,7 +34,7 @@
class FakeMediaTttChipControllerReceiver(
commandQueue: CommandQueue,
context: Context,
- logger: MediaTttLogger<ChipReceiverInfo>,
+ logger: MediaTttReceiverLogger,
windowManager: WindowManager,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 5e40898..dba2da7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -36,7 +36,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -68,7 +67,7 @@
@Mock
private lateinit var applicationInfo: ApplicationInfo
@Mock
- private lateinit var logger: MediaTttLogger<ChipReceiverInfo>
+ private lateinit var logger: MediaTttReceiverLogger
@Mock
private lateinit var accessibilityManager: AccessibilityManager
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
index 0e7bf8d..95df484 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.receiver
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -31,16 +30,17 @@
import org.mockito.Mockito.mock
@SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttReceiverLoggerTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
- private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+ private lateinit var logger: MediaTttReceiverLogger
@Before
- fun setUp () {
- buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+ fun setUp() {
+ buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ logger = MediaTttReceiverLogger(buffer)
}
@Test
@@ -52,13 +52,20 @@
logger.logStateChange(stateName, id, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(DEVICE_TYPE_TAG)
assertThat(actualString).contains(stateName)
assertThat(actualString).contains(id)
assertThat(actualString).contains(packageName)
}
@Test
+ fun logStateChangeError_hasState() {
+ logger.logStateChangeError(3456)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("3456")
+ }
+
+ @Test
fun logPackageNotFound_bufferHasPackageName() {
val packageName = "this.is.a.package"
@@ -68,23 +75,9 @@
assertThat(actualString).contains(packageName)
}
- @Test
- fun logRemovalBypass_bufferHasReasons() {
- val removalReason = "fakeRemovalReason"
- val bypassReason = "fakeBypassReason"
-
- logger.logRemovalBypass(removalReason, bypassReason)
-
- val actualString = getStringFromBuffer()
- assertThat(actualString).contains(removalReason)
- assertThat(actualString).contains(bypassReason)
- }
-
private fun getStringFromBuffer(): String {
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
return stringWriter.toString()
}
}
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 54d4460..c63ca3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -40,16 +40,13 @@
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
-import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -92,7 +89,7 @@
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var chipbarLogger: ChipbarLogger
- @Mock private lateinit var logger: MediaTttLogger<ChipbarInfo>
+ @Mock private lateinit var logger: MediaTttSenderLogger
@Mock private lateinit var mediaTttFlags: MediaTttFlags
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var powerManager: PowerManager
@@ -139,7 +136,7 @@
uiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
chipbarCoordinator =
- FakeChipbarCoordinator(
+ ChipbarCoordinator(
context,
chipbarLogger,
windowManager,
@@ -163,6 +160,7 @@
chipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -181,6 +179,7 @@
chipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -542,6 +541,7 @@
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
verify(windowManager).removeView(viewCaptor.value)
+ verify(logger).logStateMapRemoval(eq(DEFAULT_ID), any())
}
@Test
@@ -741,6 +741,99 @@
verify(windowManager, never()).addView(any(), any())
}
+ /** Regression test for b/266217596. */
+ @Test
+ fun toReceiver_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ routeInfo,
+ null,
+ )
+
+ // WHEN a FAR command comes in
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null,
+ )
+
+ // THEN it is ignored and the chipbar is stilled displayed
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+ verify(windowManager, never()).removeView(any())
+
+ // WHEN a SUCCEEDED command comes in
+ val succeededRouteInfo =
+ MediaRoute2Info.Builder(DEFAULT_ID, "Tablet Succeeded")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ succeededRouteInfo,
+ /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ },
+ )
+
+ // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+ // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(
+ ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.getExpectedStateText(
+ "Tablet Succeeded"
+ )
+ )
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+ assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ /** Regression test for b/266217596. */
+ @Test
+ fun toThisDevice_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ routeInfo,
+ null,
+ )
+
+ // WHEN a FAR command comes in
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null,
+ )
+
+ // THEN it is ignored and the chipbar is stilled displayed
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+ verify(windowManager, never()).removeView(any())
+
+ // WHEN a SUCCEEDED command comes in
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ routeInfo,
+ /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ },
+ )
+
+ // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+ // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(
+ ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.getExpectedStateText(
+ "Tablet Succeeded"
+ )
+ )
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+ assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ }
+
@Test
fun receivesNewStateFromCommandQueue_isLogged() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
@@ -934,6 +1027,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -960,6 +1054,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -977,9 +1072,10 @@
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
// WHEN the listener is notified that the view has been removed
- listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID)
+ listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID, "reason")
// THEN the media coordinator unregisters the listener
+ verify(logger).logStateMapRemoval(DEFAULT_ID, "reason")
verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
}
@@ -991,6 +1087,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -1008,7 +1105,7 @@
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
// WHEN the listener is notified that a different view has been removed
- listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId")
+ listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId", "reason")
// THEN the media coordinator doesn't unregister the listener
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
@@ -1022,6 +1119,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -1057,6 +1155,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -1086,10 +1185,173 @@
verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
// THEN one of them is removed
- listenerCaptor.value.onInfoPermanentlyRemoved("route1")
+ listenerCaptor.value.onInfoPermanentlyRemoved("route1", "reason")
// THEN the media coordinator doesn't unregister the listener (since route2 is still active)
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
+ verify(logger).logStateMapRemoval("route1", "reason")
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun twoIdsDisplayed_oldIdIsFar_viewStillDisplayed() {
+ // WHEN there are two different media transfers with different IDs
+ val route1 =
+ MediaRoute2Info.Builder("route1", OTHER_DEVICE_NAME)
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ route1,
+ null,
+ )
+ verify(windowManager).addView(any(), any())
+ reset(windowManager)
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ MediaRoute2Info.Builder("route2", "Route 2 name")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build(),
+ null,
+ )
+ val newView = getChipbarView()
+
+ // WHEN there's a FAR event for the earlier one
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ route1,
+ null,
+ )
+
+ // THEN it's ignored and the more recent one is still displayed
+ assertThat(newView.getChipText())
+ .isEqualTo(
+ ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText("Route 2 name")
+ )
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToEnd() {
+ displayReceiverTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show ALMOST_CLOSE_TO_END
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ routeInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.getExpectedStateText())
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayReceiverTriggered() {
+ displayReceiverTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show RECEIVER_TRIGGERED
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ routeInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToStart() {
+ displayThisDeviceTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show ALMOST_CLOSE_TO_START
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ routeInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText())
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayThisDeviceTriggered() {
+ displayThisDeviceTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show THIS_DEVICE_TRIGGERED
+ val newRouteInfo =
+ MediaRoute2Info.Builder(DEFAULT_ID, "New Name")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ newRouteInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(
+ ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText("New Name")
+ )
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
}
private fun getChipbarView(): ViewGroup {
@@ -1109,8 +1371,10 @@
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
- private fun ChipStateSender.getExpectedStateText(): String? {
- return this.getChipTextString(context, OTHER_DEVICE_NAME).loadText(context)
+ private fun ChipStateSender.getExpectedStateText(
+ otherDeviceName: String = OTHER_DEVICE_NAME,
+ ): String? {
+ return this.getChipTextString(context, otherDeviceName).loadText(context)
}
// display receiver triggered state helper method to make sure we start from a valid state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
similarity index 62%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
index 0e7bf8d..0033757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.sender
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -31,16 +30,17 @@
import org.mockito.Mockito.mock
@SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttSenderLoggerTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
- private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+ private lateinit var logger: MediaTttSenderLogger
@Before
- fun setUp () {
- buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+ fun setUp() {
+ buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ logger = MediaTttSenderLogger(buffer)
}
@Test
@@ -52,13 +52,20 @@
logger.logStateChange(stateName, id, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(DEVICE_TYPE_TAG)
assertThat(actualString).contains(stateName)
assertThat(actualString).contains(id)
assertThat(actualString).contains(packageName)
}
@Test
+ fun logStateChangeError_hasState() {
+ logger.logStateChangeError(3456)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("3456")
+ }
+
+ @Test
fun logPackageNotFound_bufferHasPackageName() {
val packageName = "this.is.a.package"
@@ -80,11 +87,35 @@
assertThat(actualString).contains(bypassReason)
}
+ @Test
+ fun logStateMap_bufferHasInfo() {
+ val map =
+ mapOf(
+ "123" to ChipStateSender.ALMOST_CLOSE_TO_START_CAST,
+ "456" to ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ )
+
+ logger.logStateMap(map)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("123")
+ assertThat(actualString).contains(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
+ assertThat(actualString).contains("456")
+ assertThat(actualString).contains(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
+ }
+
+ @Test
+ fun logStateMapRemoval_bufferHasInfo() {
+ logger.logStateMapRemoval("456", "testReason")
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("456")
+ assertThat(actualString).contains("testReason")
+ }
+
private fun getStringFromBuffer(): String {
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
return stringWriter.toString()
}
}
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
index 9bcfd5b..1a93adc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
@@ -26,6 +26,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeDisplayTracker;
import org.junit.Before;
import org.junit.Test;
@@ -46,7 +47,8 @@
@Before
public void setup() {
- mFlagsContainer = new SysUiState();
+ FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
+ mFlagsContainer = new SysUiState(displayTracker);
mCallback = mock(SysUiState.SysUiStateCallback.class);
mFlagsContainer.addCallback(mCallback);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
index 92652a7..3eb7329 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.After;
@@ -65,6 +66,7 @@
private ImageReader mReader;
private NavigationBarView mNavBar;
private VirtualDisplay mVirtualDisplay;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@Before
public void setup() {
@@ -83,6 +85,7 @@
mDependency.injectTestDependency(EdgeBackGestureHandler.Factory.class,
mEdgeBackGestureHandlerFactory);
mNavBar = new NavigationBarView(context, null);
+ mNavBar.setDisplayTracker(mDisplayTracker);
}
private Display createVirtualDisplay() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 8058b85..2212bbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -50,6 +50,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
@@ -81,6 +82,7 @@
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
private StaticMockitoSession mMockitoSession;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@Mock
private CommandQueue mCommandQueue;
@@ -110,7 +112,8 @@
Optional.of(mock(Pip.class)),
Optional.of(mock(BackAnimation.class)),
mock(FeatureFlags.class),
- mock(SecureSettings.class)));
+ mock(SecureSettings.class),
+ mDisplayTracker));
initializeNavigationBars();
mMockitoSession = mockitoSession().mockStatic(Utilities.class).startMocking();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 2ad865e..764ddc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -87,6 +87,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowView;
@@ -495,7 +496,8 @@
Optional.of(mock(BackAnimation.class)),
mUserContextProvider,
mWakefulnessLifecycle,
- mTaskStackChangeListeners));
+ mTaskStackChangeListeners,
+ new FakeDisplayTracker(mContext)));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index cafd2cf..54e6509 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -36,6 +36,7 @@
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -63,6 +64,7 @@
IWindowManager mIWindowManager;
private NavigationBarTransitions mTransitions;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@Before
public void setup() {
@@ -86,7 +88,7 @@
when(navBar.getCurrentView()).thenReturn(navBar);
when(navBar.findViewById(anyInt())).thenReturn(navBar);
mTransitions = new NavigationBarTransitions(
- navBar, mIWindowManager, mLightBarTransitionsFactory);
+ navBar, mIWindowManager, mLightBarTransitionsFactory, mDisplayTracker);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
index bbe60f4..18be92b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
@@ -16,17 +16,14 @@
package com.android.systemui.notetask
-import android.content.ComponentName
+import android.app.role.RoleManager
import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ResolveInfoFlags
-import android.content.pm.ResolveInfo
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -47,172 +44,39 @@
internal class NoteTaskIntentResolverTest : SysuiTestCase() {
@Mock lateinit var packageManager: PackageManager
+ @Mock lateinit var roleManager: RoleManager
- private lateinit var resolver: NoteTaskIntentResolver
+ private lateinit var underTest: NoteTaskIntentResolver
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- resolver = NoteTaskIntentResolver(packageManager)
- }
-
- private fun createResolveInfo(
- activityInfo: ActivityInfo? = createActivityInfo(),
- ): ResolveInfo {
- return ResolveInfo().apply { this.activityInfo = activityInfo }
- }
-
- private fun createActivityInfo(
- packageName: String = "PackageName",
- name: String? = "ActivityName",
- exported: Boolean = true,
- enabled: Boolean = true,
- showWhenLocked: Boolean = true,
- turnScreenOn: Boolean = true,
- ): ActivityInfo {
- return ActivityInfo().apply {
- this.name = name
- this.exported = exported
- this.enabled = enabled
- if (showWhenLocked) {
- flags = flags or ActivityInfo.FLAG_SHOW_WHEN_LOCKED
- }
- if (turnScreenOn) {
- flags = flags or ActivityInfo.FLAG_TURN_SCREEN_ON
- }
- this.applicationInfo = ApplicationInfo().apply { this.packageName = packageName }
- }
- }
-
- private fun givenQueryIntentActivities(block: () -> List<ResolveInfo>) {
- whenever(packageManager.queryIntentActivities(any(), any<ResolveInfoFlags>()))
- .thenReturn(block())
- }
-
- private fun givenResolveActivity(block: () -> ResolveInfo?) {
- whenever(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenReturn(block())
+ underTest = NoteTaskIntentResolver(context, roleManager)
}
@Test
- fun resolveIntent_shouldReturnNotesIntent() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo()) }
+ fun resolveIntent_shouldReturnIntentInStylusMode() {
+ val packageName = "com.android.note.app"
+ whenever(roleManager.getRoleHoldersAsUser(NoteTaskIntentResolver.ROLE_NOTES, context.user))
+ .then { listOf(packageName) }
- val actual = resolver.resolveIntent()
+ val actual = underTest.resolveIntent()
- val expected =
- Intent(ACTION_CREATE_NOTE)
- .setPackage("PackageName")
- .setComponent(ComponentName("PackageName", "ActivityName"))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- // Compares the string representation of both intents, as they are different instances.
- assertThat(actual.toString()).isEqualTo(expected.toString())
+ requireNotNull(actual) { "Intent must not be null" }
+ assertThat(actual.action).isEqualTo(ACTION_CREATE_NOTE)
+ assertThat(actual.`package`).isEqualTo(packageName)
+ val expectedExtra = actual.getExtra(NoteTaskIntentResolver.INTENT_EXTRA_USE_STYLUS_MODE)
+ assertThat(expectedExtra).isEqualTo(true)
+ val expectedFlag = actual.flags and Intent.FLAG_ACTIVITY_NEW_TASK
+ assertThat(expectedFlag).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
}
@Test
- fun resolveIntent_activityInfoEnabledIsFalse_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity {
- createResolveInfo(activityInfo = createActivityInfo(enabled = false))
- }
+ fun resolveIntent_noRoleHolderIsSet_shouldReturnNull() {
+ whenever(roleManager.getRoleHoldersAsUser(eq(NoteTaskIntentResolver.ROLE_NOTES), any()))
+ .then { listOf<String>() }
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_activityInfoExportedIsFalse_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity {
- createResolveInfo(activityInfo = createActivityInfo(exported = false))
- }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_activityInfoShowWhenLockedIsFalse_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity {
- createResolveInfo(activityInfo = createActivityInfo(showWhenLocked = false))
- }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_activityInfoTurnScreenOnIsFalse_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity {
- createResolveInfo(activityInfo = createActivityInfo(turnScreenOn = false))
- }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_activityInfoNameIsBlank_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo(name = "")) }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_activityInfoNameIsNull_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo(name = null)) }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_activityInfoIsNull_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity { createResolveInfo(activityInfo = null) }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_resolveActivityIsNull_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo()) }
- givenResolveActivity { null }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_packageNameIsBlank_shouldReturnNull() {
- givenQueryIntentActivities {
- listOf(createResolveInfo(createActivityInfo(packageName = "")))
- }
-
- val actual = resolver.resolveIntent()
-
- assertThat(actual).isNull()
- }
-
- @Test
- fun resolveIntent_activityNotFoundForAction_shouldReturnNull() {
- givenQueryIntentActivities { emptyList() }
-
- val actual = resolver.resolveIntent()
+ val actual = underTest.resolveIntent()
assertThat(actual).isNull()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 43fb1bd..dee1cc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -59,6 +59,7 @@
@SmallTest
public class AutoAddTrackerTest extends SysuiTestCase {
+ private static final int END_POSITION = -1;
private static final int USER = 0;
@Mock
@@ -142,6 +143,29 @@
}
@Test
+ public void testRestoredTilePositionPreserved() {
+ verify(mBroadcastDispatcher).registerReceiver(
+ mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+ String restoredTiles = "saver,internet,work,cast";
+ Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ assertEquals(2, mAutoTracker.getRestoredTilePosition("work"));
+ }
+
+ @Test
+ public void testNoRestoredTileReturnsEndPosition() {
+ verify(mBroadcastDispatcher).registerReceiver(
+ mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+ Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, null);
+
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ assertEquals(END_POSITION, mAutoTracker.getRestoredTilePosition("work"));
+ }
+
+ @Test
public void testBroadcastReceiverRegistered() {
verify(mBroadcastDispatcher).registerReceiver(
any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index bbd62c7..6f54f62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -147,16 +147,25 @@
setUserProfiles(0);
setShowUserVisibleJobs(true);
- UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, 0, "pkg1", null, 0);
- UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, 0, "pkg2", null, 1);
+ UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, "pkg1", 0, "pkg1", null, 0);
+ UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, "pkg2", 0, "pkg2", null, 1);
+ // pkg2 is performing work on behalf of pkg3. Since pkg2 will show the notification
+ // It should be the one shown in TaskManager.
+ UserVisibleJobSummary j3 = new UserVisibleJobSummary(1, "pkg2", 0, "pkg3", null, 3);
Assert.assertEquals(0, mFmc.getNumRunningPackages());
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, true);
Assert.assertEquals(2, mFmc.getNumRunningPackages());
+ // Job3 starts running. The source package (pkg3) shouldn't matter. Since pkg2 is
+ // already running, the running package count shouldn't increase.
+ mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, true);
+ Assert.assertEquals(2, mFmc.getNumRunningPackages());
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, false);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, false);
+ Assert.assertEquals(1, mFmc.getNumRunningPackages());
+ mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, false);
Assert.assertEquals(0, mFmc.getNumRunningPackages());
}
@@ -167,8 +176,8 @@
Binder b1 = new Binder();
Binder b2 = new Binder();
- UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, 0, "pkg1", null, 0);
- UserVisibleJobSummary j3 = new UserVisibleJobSummary(1, 0, "pkg3", null, 1);
+ UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, "pkg1", 0, "pkg1", null, 0);
+ UserVisibleJobSummary j3 = new UserVisibleJobSummary(1, "pkg3", 0, "pkg3", null, 1);
Assert.assertEquals(0, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
@@ -359,8 +368,8 @@
// pkg1 has only job
// pkg2 has both job and fgs
// pkg3 has only fgs
- UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, 0, "pkg1", null, 0);
- UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, 0, "pkg2", null, 1);
+ UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, "pkg1", 0, "pkg1", null, 0);
+ UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, "pkg2", 0, "pkg2", null, 1);
Binder b2 = new Binder();
Binder b3 = new Binder();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 42ef9c2..4caa50f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -60,6 +60,7 @@
import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -494,7 +495,7 @@
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
MockitoAnnotations.initMocks(this);
- CommandQueue commandQueue = new CommandQueue(context);
+ CommandQueue commandQueue = new CommandQueue(context, new FakeDisplayTracker(context));
setupQsComponent();
setUpViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 2bd068a..a7733a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -41,6 +41,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
@@ -87,6 +88,7 @@
@Mock private lateinit var serviceInfo: ServiceInfo
@Mock private lateinit var customTileStatePersister: CustomTileStatePersister
+ private var displayTracker = FakeDisplayTracker(mContext)
private lateinit var customTile: CustomTile
private lateinit var testableLooper: TestableLooper
private lateinit var customTileBuilder: CustomTile.Builder
@@ -119,7 +121,8 @@
activityStarter,
qsLogger,
customTileStatePersister,
- tileServices
+ tileServices,
+ displayTracker
)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
index b6a595b..7ba2cf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
@@ -35,9 +35,33 @@
@Test
fun testCreateShareIntent() {
val uri = Uri.parse("content://fake")
+
+ val output = ActionIntentCreator.createShareIntent(uri)
+
+ assertThat(output.action).isEqualTo(Intent.ACTION_CHOOSER)
+ assertFlagsSet(
+ Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_CLEAR_TASK or
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ output.flags
+ )
+
+ val wrappedIntent = output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertThat(wrappedIntent?.action).isEqualTo(Intent.ACTION_SEND)
+ assertThat(wrappedIntent?.data).isEqualTo(uri)
+ assertThat(wrappedIntent?.type).isEqualTo("image/png")
+ assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_SUBJECT)).isNull()
+ assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_TEXT)).isNull()
+ assertThat(wrappedIntent?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
+ .isEqualTo(uri)
+ }
+
+ @Test
+ fun testCreateShareIntentWithSubject() {
+ val uri = Uri.parse("content://fake")
val subject = "Example subject"
- val output = ActionIntentCreator.createShareIntent(uri, subject)
+ val output = ActionIntentCreator.createShareIntentWithSubject(uri, subject)
assertThat(output.action).isEqualTo(Intent.ACTION_CHOOSER)
assertFlagsSet(
@@ -52,16 +76,34 @@
assertThat(wrappedIntent?.data).isEqualTo(uri)
assertThat(wrappedIntent?.type).isEqualTo("image/png")
assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_SUBJECT)).isEqualTo(subject)
+ assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_TEXT)).isNull()
assertThat(wrappedIntent?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
.isEqualTo(uri)
}
@Test
- fun testCreateShareIntent_noSubject() {
+ fun testCreateShareIntentWithExtraText() {
val uri = Uri.parse("content://fake")
- val output = ActionIntentCreator.createShareIntent(uri, null)
+ val extraText = "Extra text"
+
+ val output = ActionIntentCreator.createShareIntentWithExtraText(uri, extraText)
+
+ assertThat(output.action).isEqualTo(Intent.ACTION_CHOOSER)
+ assertFlagsSet(
+ Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_CLEAR_TASK or
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ output.flags
+ )
+
val wrappedIntent = output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertThat(wrappedIntent?.action).isEqualTo(Intent.ACTION_SEND)
+ assertThat(wrappedIntent?.data).isEqualTo(uri)
+ assertThat(wrappedIntent?.type).isEqualTo("image/png")
assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_SUBJECT)).isNull()
+ assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(extraText)
+ assertThat(wrappedIntent?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
+ .isEqualTo(uri)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index e1eda11..7d58325 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -39,6 +39,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -67,6 +68,7 @@
private PendingIntent mMockPendingIntent;
private Intent mIntent;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@Before
public void setup() throws InterruptedException, ExecutionException, TimeoutException {
@@ -135,10 +137,11 @@
if (withStatusBar) {
return new ActionProxyReceiver(
Optional.of(mMockCentralSurfaces), mMockActivityManagerWrapper,
- mMockScreenshotSmartActions);
+ mMockScreenshotSmartActions, mDisplayTracker);
} else {
return new ActionProxyReceiver(
- Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions);
+ Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions,
+ mDisplayTracker);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index ed3f1a0..541d6c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -73,6 +73,30 @@
assertThat(result).isEqualTo(request)
}
+ /** Tests the Java-compatible function wrapper, ensures callback is invoked. */
+ @Test
+ fun testProcessAsync_ScreenshotData() {
+ flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+
+ val request = ScreenshotData.fromRequest(
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build())
+ val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+ var result: ScreenshotData? = null
+ var callbackCount = 0
+ val callback: (ScreenshotData) -> Unit = { processedRequest: ScreenshotData ->
+ result = processedRequest
+ callbackCount++
+ }
+
+ // runs synchronously, using Unconfined Dispatcher
+ processor.processAsync(request, callback)
+
+ // Callback invoked once returning the same request (no changes)
+ assertThat(callbackCount).isEqualTo(1)
+ assertThat(result).isEqualTo(request)
+ }
+
@Test
fun testFullScreenshot_workProfilePolicyDisabled() = runBlocking {
flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
@@ -85,6 +109,11 @@
// No changes
assertThat(processedRequest).isEqualTo(request)
+
+ val screenshotData = ScreenshotData.fromRequest(request)
+ val processedData = processor.process(screenshotData)
+
+ assertThat(processedData).isEqualTo(screenshotData)
}
@Test
@@ -108,6 +137,13 @@
assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
assertThat(processedRequest.source).isEqualTo(SCREENSHOT_OTHER)
assertThat(processedRequest.topComponent).isEqualTo(component)
+
+ val processedData = processor.process(ScreenshotData.fromRequest(request))
+
+ // Request has topComponent added, but otherwise unchanged.
+ assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
+ assertThat(processedData.source).isEqualTo(SCREENSHOT_OTHER)
+ assertThat(processedData.topComponent).isEqualTo(component)
}
@Test
@@ -140,6 +176,18 @@
assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
assertThat(processedRequest.userId).isEqualTo(USER_ID)
assertThat(processedRequest.topComponent).isEqualTo(component)
+
+ val processedData = processor.process(ScreenshotData.fromRequest(request))
+
+ // Expect a task snapshot is taken, overriding the full screen mode
+ assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
+ assertThat(processedData.bitmap).isEqualTo(bitmap)
+ assertThat(processedData.screenBounds).isEqualTo(bounds)
+ assertThat(processedData.insets).isEqualTo(Insets.NONE)
+ assertThat(processedData.taskId).isEqualTo(TASK_ID)
+ assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
+ assertThat(processedRequest.userId).isEqualTo(USER_ID)
+ assertThat(processedRequest.topComponent).isEqualTo(component)
}
@Test
@@ -165,6 +213,11 @@
// No changes
assertThat(processedRequest).isEqualTo(request)
+
+ val screenshotData = ScreenshotData.fromRequest(request)
+ val processedData = processor.process(screenshotData)
+
+ assertThat(processedData).isEqualTo(screenshotData)
}
@Test
@@ -192,6 +245,11 @@
// No changes
assertThat(processedRequest).isEqualTo(request)
+
+ val screenshotData = ScreenshotData.fromRequest(request)
+ val processedData = processor.process(screenshotData)
+
+ assertThat(processedData).isEqualTo(screenshotData)
}
@Test
@@ -220,6 +278,11 @@
// Work profile, but already a task snapshot, so no changes
assertThat(processedRequest).isEqualTo(request)
+
+ val screenshotData = ScreenshotData.fromRequest(request)
+ val processedData = processor.process(screenshotData)
+
+ assertThat(processedData).isEqualTo(screenshotData)
}
private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
new file mode 100644
index 0000000..43e9939
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.screenshot
+
+import android.content.ComponentName
+import android.graphics.Insets
+import android.graphics.Rect
+import android.os.UserHandle
+import android.view.WindowManager
+import com.android.internal.util.ScreenshotRequest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class ScreenshotDataTest {
+ private val type = WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+ private val source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
+ private val bounds = Rect(1, 2, 3, 4)
+ private val taskId = 123
+ private val userId = 1
+ private val insets = Insets.of(1, 2, 3, 4)
+ private val component = ComponentName("android.test", "android.test.Component")
+
+ @Test
+ fun testConstruction() {
+ val request =
+ ScreenshotRequest.Builder(type, source)
+ .setBoundsOnScreen(bounds)
+ .setInsets(insets)
+ .setTaskId(taskId)
+ .setUserId(userId)
+ .setTopComponent(component)
+ .build()
+
+ val data = ScreenshotData.fromRequest(request)
+
+ assertThat(data.source).isEqualTo(source)
+ assertThat(data.type).isEqualTo(type)
+ assertThat(data.screenBounds).isEqualTo(bounds)
+ assertThat(data.insets).isEqualTo(insets)
+ assertThat(data.taskId).isEqualTo(taskId)
+ assertThat(data.userHandle).isEqualTo(UserHandle.of(userId))
+ assertThat(data.topComponent).isEqualTo(component)
+ }
+
+ @Test
+ fun testNegativeUserId() {
+ val request = ScreenshotRequest.Builder(type, source).setUserId(-1).build()
+
+ val data = ScreenshotData.fromRequest(request)
+
+ assertThat(data.userHandle).isNull()
+ }
+
+ @Test
+ fun testPackageNameAsString() {
+ val request = ScreenshotRequest.Builder(type, source).setTopComponent(component).build()
+
+ val data = ScreenshotData.fromRequest(request)
+
+ assertThat(data.packageNameString).isEqualTo("android.test")
+ }
+
+ @Test
+ fun testPackageNameAsString_null() {
+ val request = ScreenshotRequest.Builder(type, source).build()
+
+ val data = ScreenshotData.fromRequest(request)
+
+ assertThat(data.packageNameString).isEqualTo("")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
index 17396b1..ee61f57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
@@ -31,6 +31,7 @@
import android.testing.AndroidTestingRunner
import com.android.systemui.SysuiTestCase
import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
+import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
@@ -126,8 +127,10 @@
val userManager = mock<UserManager>()
val atmService = mock<IActivityTaskManager>()
val dispatcher = Dispatchers.Unconfined
+ val displayTracker = FakeDisplayTracker(context)
- return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher) {
+ return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher,
+ displayTracker) {
override suspend fun isManagedProfile(userId: Int) = (userId == MANAGED_PROFILE_USER)
override suspend fun getAllRootTaskInfosOnDisplay(displayId: Int) = tasks
override suspend fun isNotificationShadeExpanded() = shadeExpanded
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index f935019..74969d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -29,7 +29,7 @@
import android.os.UserHandle
import android.os.UserManager
import android.testing.AndroidTestingRunner
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
@@ -39,7 +39,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY
-import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_CHORD
+import com.android.systemui.flags.Flags.SCREENSHOT_METADATA
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
import com.android.systemui.util.mockito.any
@@ -106,15 +107,22 @@
// Stub request processor as a synchronous no-op for tests with the flag enabled
doAnswer {
- val request: ScreenshotRequest = it.getArgument(0) as ScreenshotRequest
- val consumer: Consumer<ScreenshotRequest> = it.getArgument(1)
- consumer.accept(request)
- }
- .`when`(requestProcessor)
- .processAsync(/* request= */ any(), /* callback= */ any())
+ val request: ScreenshotRequest = it.getArgument(0) as ScreenshotRequest
+ val consumer: Consumer<ScreenshotRequest> = it.getArgument(1)
+ consumer.accept(request)
+ }.`when`(requestProcessor).processAsync(
+ /* request= */ any(ScreenshotRequest::class.java), /* callback= */ any())
+
+ doAnswer {
+ val request: ScreenshotData = it.getArgument(0) as ScreenshotData
+ val consumer: Consumer<ScreenshotData> = it.getArgument(1)
+ consumer.accept(request)
+ }.`when`(requestProcessor).processAsync(
+ /* screenshot= */ any(ScreenshotData::class.java), /* callback= */ any())
// Flipped in selected test cases
flags.set(SCREENSHOT_WORK_PROFILE_POLICY, false)
+ flags.set(SCREENSHOT_METADATA, false)
service.attach(
mContext,
@@ -141,7 +149,7 @@
@Test
fun takeScreenshotFullscreen() {
val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
.setTopComponent(topComponent)
.build()
@@ -157,16 +165,34 @@
assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
val logEvent = eventLogger.get(0)
- assertEquals(
- "Expected SCREENSHOT_REQUESTED UiEvent",
- logEvent.eventId,
- SCREENSHOT_REQUESTED_KEY_CHORD.id
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- eventLogger.get(0).packageName
- )
+ assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_KEY_OTHER.id)
+ assertEquals("Expected supplied package name",
+ topComponent.packageName, eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshotFullscreen_screenshotDataEnabled() {
+ flags.set(SCREENSHOT_METADATA, true)
+
+ val request = ScreenshotRequest.Builder(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_OTHER).setTopComponent(topComponent).build()
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller, times(1)).handleScreenshot(
+ eq(ScreenshotData.fromRequest(request)),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_KEY_OTHER.id)
+ assertEquals("Expected supplied package name",
+ topComponent.packageName, eventLogger.get(0).packageName)
}
@Test
@@ -218,7 +244,7 @@
whenever(userManager.isUserUnlocked).thenReturn(false)
val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
.setTopComponent(topComponent)
.build()
@@ -244,7 +270,7 @@
.thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
.setTopComponent(topComponent)
.build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt
new file mode 100644
index 0000000..ae976a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings
+
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.DisplayAdjustments
+import android.view.DisplayInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DisplayTrackerImplTest : SysuiTestCase() {
+ @Mock private lateinit var displayManager: DisplayManager
+ @Mock private lateinit var handler: Handler
+
+ private val executor = Executor(Runnable::run)
+ private lateinit var mDefaultDisplay: Display
+ private lateinit var mSecondaryDisplay: Display
+ private lateinit var tracker: DisplayTrackerImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ mDefaultDisplay =
+ Display(
+ DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY,
+ DisplayInfo(),
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+ )
+ mSecondaryDisplay =
+ Display(
+ DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY + 1,
+ DisplayInfo(),
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+ )
+
+ `when`(displayManager.displays).thenReturn(arrayOf(mDefaultDisplay, mSecondaryDisplay))
+
+ tracker = DisplayTrackerImpl(displayManager, handler)
+ }
+
+ @Test
+ fun testGetDefaultDisplay() {
+ assertThat(tracker.defaultDisplayId).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun testGetAllDisplays() {
+ assertThat(tracker.allDisplays).isEqualTo(arrayOf(mDefaultDisplay, mSecondaryDisplay))
+ }
+
+ @Test
+ fun registerCallback_registersDisplayListener() {
+ tracker.addDisplayChangeCallback(TestCallback(), executor)
+ verify(displayManager).registerDisplayListener(any(), any())
+ }
+
+ @Test
+ fun registerBrightnessCallback_registersDisplayListener() {
+ tracker.addBrightnessChangeCallback(TestCallback(), executor)
+ verify(displayManager)
+ .registerDisplayListener(any(), any(), eq(EVENT_FLAG_DISPLAY_BRIGHTNESS))
+ }
+
+ @Test
+ fun unregisterCallback_displayListenerStillRegistered() {
+ val callback1 = TestCallback()
+ tracker.addDisplayChangeCallback(callback1, executor)
+ tracker.addDisplayChangeCallback(TestCallback(), executor)
+ tracker.removeCallback(callback1)
+
+ verify(displayManager, never()).unregisterDisplayListener(any())
+ }
+
+ @Test
+ fun unregisterLastCallback_unregistersDisplayListener() {
+ val callback = TestCallback()
+ tracker.addDisplayChangeCallback(callback, executor)
+ tracker.removeCallback(callback)
+
+ verify(displayManager).unregisterDisplayListener(any())
+ }
+
+ @Test
+ fun callbackCalledOnDisplayAdd() {
+ val testDisplay = 2
+ val callback = TestCallback()
+ tracker.addDisplayChangeCallback(callback, executor)
+ tracker.displayChangedListener.onDisplayAdded(testDisplay)
+
+ assertThat(callback.lastDisplayAdded).isEqualTo(testDisplay)
+ }
+
+ @Test
+ fun callbackCalledOnDisplayRemoved() {
+ val testDisplay = 2
+ val callback = TestCallback()
+ tracker.addDisplayChangeCallback(callback, executor)
+ tracker.displayChangedListener.onDisplayRemoved(testDisplay)
+
+ assertThat(callback.lastDisplayRemoved).isEqualTo(testDisplay)
+ }
+
+ @Test
+ fun callbackCalledOnDisplayChanged() {
+ val testDisplay = 2
+ val callback = TestCallback()
+ tracker.addDisplayChangeCallback(callback, executor)
+ tracker.displayChangedListener.onDisplayChanged(testDisplay)
+
+ assertThat(callback.lastDisplayChanged).isEqualTo(testDisplay)
+ }
+
+ @Test
+ fun callbackCalledOnBrightnessChanged() {
+ val testDisplay = 2
+ val callback = TestCallback()
+ tracker.addBrightnessChangeCallback(callback, executor)
+ tracker.displayBrightnessChangedListener.onDisplayChanged(testDisplay)
+
+ assertThat(callback.lastDisplayChanged).isEqualTo(testDisplay)
+ }
+
+ private class TestCallback : DisplayTracker.Callback {
+ var lastDisplayAdded = -1
+ var lastDisplayRemoved = -1
+ var lastDisplayChanged = -1
+
+ override fun onDisplayAdded(displayId: Int) {
+ lastDisplayAdded = displayId
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {
+ lastDisplayRemoved = displayId
+ }
+
+ override fun onDisplayChanged(displayId: Int) {
+ lastDisplayChanged = displayId
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 9d1802a..7646193 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -28,6 +28,7 @@
import androidx.test.runner.intercepting.SingleActivityFactory
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
@@ -52,6 +53,8 @@
@Mock private lateinit var backgroundHandler: Handler
@Mock private lateinit var brightnessSliderController: BrightnessSliderController
+ private var displayTracker = FakeDisplayTracker(mContext)
+
@Rule
@JvmField
var activityRule =
@@ -60,6 +63,7 @@
override fun create(intent: Intent?): TestDialog {
return TestDialog(
userTracker,
+ displayTracker,
brightnessSliderControllerFactory,
mainExecutor,
backgroundHandler
@@ -105,12 +109,14 @@
class TestDialog(
userTracker: UserTracker,
+ displayTracker: FakeDisplayTracker,
brightnessSliderControllerFactory: BrightnessSliderController.Factory,
mainExecutor: Executor,
backgroundHandler: Handler
) :
BrightnessDialog(
userTracker,
+ displayTracker,
brightnessSliderControllerFactory,
mainExecutor,
backgroundHandler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index ed9baf5b1..3706859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -90,7 +90,7 @@
fun testEdgeElementsAlignedWithEdgeOrGuide_qs() {
with(qsConstraint) {
assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID)
- assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+ assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f)
assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f)
@@ -342,7 +342,6 @@
R.id.clock to "clock",
R.id.date to "date",
R.id.privacy_container to "privacy",
- R.id.carrier_group to "carriers",
)
views.forEach { (id, name) ->
assertWithMessage("$name has 0 height in qqs")
@@ -361,7 +360,6 @@
val views = mapOf(
R.id.clock to "clock",
R.id.privacy_container to "privacy",
- R.id.carrier_group to "carriers",
)
views.forEach { (id, name) ->
expect.withMessage("$name changes height")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index f580f5e..91fef1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -692,6 +692,19 @@
assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
}
+ @Test
+ fun `carrier left padding is set when clock layout changes`() {
+ val width = 200
+ whenever(clock.width).thenReturn(width)
+ whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml
+ val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java)
+
+ verify(clock).addOnLayoutChangeListener(capture(captor))
+ captor.value.onLayoutChange(clock, 0, 0, width, 0, 0, 0, 0, 0)
+
+ verify(carrierGroup).setPaddingRelative(514, 0, 0, 0)
+ }
+
private fun View.executeLayoutChange(
left: Int,
top: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index b568122..2bf2a81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.shade
+import android.animation.Animator
import android.animation.ValueAnimator
import android.app.StatusBarManager
import android.content.Context
@@ -45,8 +46,8 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -293,6 +294,34 @@
}
@Test
+ fun animatorListenersClearedAtEnd() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
+ val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+ verify(animator).setListener(capture(listenerCaptor))
+
+ listenerCaptor.value.onAnimationEnd(mock())
+ verify(animator).setListener(null)
+ verify(animator).setUpdateListener(null)
+ }
+
+ @Test
+ fun animatorListenersClearedOnCancel() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
+ val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+ verify(animator).setListener(capture(listenerCaptor))
+
+ listenerCaptor.value.onAnimationCancel(mock())
+ verify(animator).setListener(null)
+ verify(animator).setUpdateListener(null)
+ }
+
+ @Test
fun demoMode_attachDemoMode() {
val cb = argumentCaptor<DemoMode>()
verify(demoModeController).addCallback(capture(cb))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 6dd2d61..20f3cb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -97,6 +97,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.common.ui.view.LongPressHandlingView;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -105,10 +106,12 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
@@ -197,7 +200,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NotificationPanelViewControllerTest extends SysuiTestCase {
private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
@@ -303,6 +306,8 @@
@Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock private KeyguardInteractor mKeyguardInteractor;
+ @Mock private KeyguardLongPressViewModel mKeyuardLongPressViewModel;
@Mock private CoroutineDispatcher mMainDispatcher;
@Mock private MotionEvent mDownMotionEvent;
@Captor
@@ -459,6 +464,9 @@
mMainHandler = new Handler(Looper.getMainLooper());
+ when(mView.requireViewById(R.id.keyguard_long_press))
+ .thenReturn(mock(LongPressHandlingView.class));
+
mNotificationPanelViewController = new NotificationPanelViewController(
mView,
mMainHandler,
@@ -528,7 +536,9 @@
mLockscreenToOccludedTransitionViewModel,
mMainDispatcher,
mKeyguardTransitionInteractor,
- mDumpManager);
+ mDumpManager,
+ mKeyuardLongPressViewModel,
+ mKeyguardInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
index 5a62cc1..ae1c8cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
@@ -1,21 +1,17 @@
package com.android.systemui.shared.regionsampling
-import android.graphics.Rect
+import android.app.WallpaperManager
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper
import java.io.PrintWriter
import java.util.concurrent.Executor
-import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
@@ -28,9 +24,8 @@
@Mock private lateinit var sampledView: View
@Mock private lateinit var mainExecutor: Executor
@Mock private lateinit var bgExecutor: Executor
- @Mock private lateinit var regionSampler: RegionSamplingHelper
@Mock private lateinit var pw: PrintWriter
- @Mock private lateinit var callback: RegionSamplingHelper.SamplingCallback
+ @Mock private lateinit var wallpaperManager: WallpaperManager
private lateinit var mRegionSampler: RegionSampler
private var updateFun: UpdateColorCallback = {}
@@ -38,65 +33,18 @@
@Before
fun setUp() {
whenever(sampledView.isAttachedToWindow).thenReturn(true)
- whenever(regionSampler.callback).thenReturn(this@RegionSamplerTest.callback)
mRegionSampler =
- object : RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun) {
- override fun createRegionSamplingHelper(
- sampledView: View,
- callback: RegionSamplingHelper.SamplingCallback,
- mainExecutor: Executor?,
- bgExecutor: Executor?
- ): RegionSamplingHelper {
- return this@RegionSamplerTest.regionSampler
- }
- }
+ RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun, wallpaperManager)
}
@Test
fun testStartRegionSampler() {
mRegionSampler.startRegionSampler()
-
- verify(regionSampler).start(Rect(0, 0, 0, 0))
- }
-
- @Test
- fun testStopRegionSampler() {
- mRegionSampler.stopRegionSampler()
-
- verify(regionSampler).stop()
}
@Test
fun testDump() {
mRegionSampler.dump(pw)
-
- verify(regionSampler).dump(pw)
- }
-
- @Test
- fun testUpdateColorCallback() {
- regionSampler.callback.onRegionDarknessChanged(false)
- verify(regionSampler.callback).onRegionDarknessChanged(false)
- clearInvocations(regionSampler.callback)
- regionSampler.callback.onRegionDarknessChanged(true)
- verify(regionSampler.callback).onRegionDarknessChanged(true)
- }
-
- @Test
- fun testFlagFalse() {
- mRegionSampler =
- object : RegionSampler(sampledView, mainExecutor, bgExecutor, false, updateFun) {
- override fun createRegionSamplingHelper(
- sampledView: View,
- callback: RegionSamplingHelper.SamplingCallback,
- mainExecutor: Executor?,
- bgExecutor: Executor?
- ): RegionSamplingHelper {
- return this@RegionSamplerTest.regionSampler
- }
- }
-
- Assert.assertEquals(mRegionSampler.regionSampler, null)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
index d29e9a6..fa7d869 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
@@ -20,8 +20,6 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.Execution
@@ -41,9 +39,6 @@
@TestableLooper.RunWithLooper
class LockscreenPreconditionTest : SysuiTestCase() {
@Mock
- private lateinit var featureFlags: FeatureFlags
-
- @Mock
private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock
@@ -64,10 +59,7 @@
fun testFullyEnabled() {
`when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
`when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
- .thenReturn(true)
- val precondition = LockscreenPrecondition(featureFlags, deviceProvisionedController,
- execution)
+ val precondition = LockscreenPrecondition(deviceProvisionedController, execution)
precondition.addListener(listener)
`verify`(listener).onCriteriaChanged()
@@ -81,10 +73,8 @@
fun testProvisioning() {
`when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
`when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
- `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
- .thenReturn(true)
val precondition =
- LockscreenPrecondition(featureFlags, deviceProvisionedController, execution)
+ LockscreenPrecondition(deviceProvisionedController, execution)
precondition.addListener(listener)
verify(listener).onCriteriaChanged()
@@ -109,10 +99,8 @@
fun testUserSetup() {
`when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
`when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
- .thenReturn(true)
val precondition =
- LockscreenPrecondition(featureFlags, deviceProvisionedController, execution)
+ LockscreenPrecondition(deviceProvisionedController, execution)
precondition.addListener(listener)
verify(listener).onCriteriaChanged()
@@ -129,4 +117,4 @@
verify(listener).onCriteriaChanged()
assertThat(precondition.conditionsMet()).isTrue()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 0000c32..b1ca1c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -46,6 +46,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import org.junit.After;
@@ -63,12 +64,13 @@
};
private CommandQueue mCommandQueue;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private Callbacks mCallbacks;
private static final int SECONDARY_DISPLAY = 1;
@Before
public void setup() {
- mCommandQueue = new CommandQueue(mContext);
+ mCommandQueue = new CommandQueue(mContext, mDisplayTracker);
mCallbacks = mock(Callbacks.class);
mCommandQueue.addCallback(mCallbacks);
verify(mCallbacks).disable(anyInt(), eq(0), eq(0), eq(false));
@@ -418,7 +420,7 @@
@Test
public void testOnDisplayRemoved() {
- mCommandQueue.onDisplayRemoved(SECONDARY_DISPLAY);
+ mDisplayTracker.triggerOnDisplayRemoved(SECONDARY_DISPLAY);
waitForIdleSync();
verify(mCallbacks).onDisplayRemoved(eq(SECONDARY_DISPLAY));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 817d6a2..0806a62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -58,6 +58,7 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.app.AlarmManager;
import android.app.Instrumentation;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
@@ -184,6 +185,8 @@
private ScreenLifecycle mScreenLifecycle;
@Mock
private AuthController mAuthController;
+ @Mock
+ private AlarmManager mAlarmManager;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -286,7 +289,9 @@
mAuthController, mLockPatternUtils, mScreenLifecycle,
mKeyguardBypassController, mAccessibilityManager,
mFaceHelpMessageDeferral, mock(KeyguardLogger.class),
- mAlternateBouncerInteractor);
+ mAlternateBouncerInteractor,
+ mAlarmManager
+ );
mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 5124eb9..e6f272b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -37,6 +37,7 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -152,4 +153,18 @@
// and cause us to drop a frame during the LOCKSCREEN_TRANSITION_FROM_AOD CUJ.
assertEquals(0.99f, controller.dozeAmount, 0.009f)
}
+
+ @Test
+ fun testSetDreamState_invokesCallback() {
+ val listener = mock(StatusBarStateController.StateListener::class.java)
+ controller.addCallback(listener)
+
+ controller.setIsDreaming(true)
+ verify(listener).onDreamingChanged(true)
+
+ Mockito.clearInvocations(listener)
+
+ controller.setIsDreaming(false)
+ verify(listener).onDreamingChanged(false)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
index ea06647..746544a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
@@ -6,6 +6,7 @@
import android.view.MotionEvent
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeDisplayTracker
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -17,9 +18,11 @@
class GenericGestureDetectorTest : SysuiTestCase() {
private lateinit var gestureDetector: TestGestureDetector
+ private lateinit var displayTracker: FakeDisplayTracker
@Before
fun setUp() {
+ displayTracker = FakeDisplayTracker(mContext)
gestureDetector = TestGestureDetector()
}
@@ -101,12 +104,21 @@
gestureDetector.addOnGestureDetectedCallback("tag2"){}
gestureDetector.removeOnGestureDetectedCallback("tag")
- gestureDetector.onInputEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0))
+ gestureDetector.onInputEvent(
+ MotionEvent.obtain(
+ 0,
+ 0,
+ MotionEvent.ACTION_DOWN,
+ CORRECT_X,
+ 0f,
+ 0
+ )
+ )
assertThat(oldCallbackNotified).isFalse()
}
- inner class TestGestureDetector : GenericGestureDetector("fakeTag") {
+ inner class TestGestureDetector : GenericGestureDetector("fakeTag", displayTracker) {
var isGestureListening = false
override fun onInputEvent(ev: InputEvent) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 4bcb54d..2423f13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -113,6 +113,9 @@
private lateinit var handler: Handler
@Mock
+ private lateinit var weatherPlugin: BcSmartspaceDataPlugin
+
+ @Mock
private lateinit var plugin: BcSmartspaceDataPlugin
@Mock
@@ -152,6 +155,7 @@
KeyguardBypassController.OnBypassStateChangedListener
private lateinit var deviceProvisionedListener: DeviceProvisionedListener
+ private lateinit var weatherSmartspaceView: SmartspaceView
private lateinit var smartspaceView: SmartspaceView
private val clock = FakeSystemClock()
@@ -177,18 +181,22 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE)).thenReturn(true)
+ // Todo(b/261760571): flip the flag value here when feature is launched, and update relevant
+ // tests.
+ `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
.thenReturn(fakePrivateLockscreenSettingUri)
`when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
.thenReturn(fakeNotifOnLockscreenSettingUri)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
+ `when`(weatherPlugin.getView(any())).thenReturn(
+ createWeatherSmartspaceView(), createWeatherSmartspaceView())
`when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
`when`(userTracker.userProfiles).thenReturn(userList)
`when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
- `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
- `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
setActiveUser(userHandlePrimary)
setAllowPrivateNotifications(userHandlePrimary, true)
@@ -213,6 +221,7 @@
executor,
bgExecutor,
handler,
+ Optional.of(weatherPlugin),
Optional.of(plugin),
Optional.of(configPlugin),
)
@@ -222,21 +231,21 @@
}
@Test(expected = RuntimeException::class)
- fun testThrowsIfFlagIsDisabled() {
+ fun testBuildAndConnectWeatherView_throwsIfDecouplingDisabled() {
// GIVEN the feature flag is disabled
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE)).thenReturn(false)
+ `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
// WHEN we try to build the view
- controller.buildAndConnectView(fakeParent)
+ controller.buildAndConnectWeatherView(fakeParent)
// THEN an exception is thrown
}
@Test
- fun connectOnlyAfterDeviceIsProvisioned() {
+ fun testBuildAndConnectView_connectsOnlyAfterDeviceIsProvisioned() {
// GIVEN an unprovisioned device and an attempt to connect
- `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false)
- `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
// WHEN a connection attempt is made and view is attached
val view = controller.buildAndConnectView(fakeParent)
@@ -246,8 +255,8 @@
verify(smartspaceManager, never()).createSmartspaceSession(any())
// WHEN it does become provisioned
- `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
- `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
deviceProvisionedListener.onUserSetupChanged()
// THEN the session is created
@@ -257,7 +266,7 @@
}
@Test
- fun testListenersAreRegistered() {
+ fun testAddListener_registersListenersForPlugin() {
// GIVEN a listener is added after a session is created
connectSession()
@@ -266,10 +275,12 @@
// THEN the listener is registered to the underlying plugin
verify(plugin).registerListener(controllerListener)
+ // The listener is registered only for the plugin, not the weather plugin.
+ verify(weatherPlugin, never()).registerListener(any())
}
@Test
- fun testEarlyRegisteredListenersAreAttachedAfterConnected() {
+ fun testAddListener_earlyRegisteredListenersAreAttachedAfterConnected() {
// GIVEN a listener that is registered before the session is created
controller.addListener(controllerListener)
@@ -278,10 +289,12 @@
// THEN the listener is subsequently registered
verify(plugin).registerListener(controllerListener)
+ // The listener is registered only for the plugin, not the weather plugin.
+ verify(weatherPlugin, never()).registerListener(any())
}
@Test
- fun testEmptyListIsEmittedAndNotifierRemovedAfterDisconnect() {
+ fun testDisconnect_emitsEmptyListAndRemovesNotifier() {
// GIVEN a registered listener on an active session
connectSession()
clearInvocations(plugin)
@@ -293,10 +306,12 @@
// THEN the listener receives an empty list of targets and unregisters the notifier
verify(plugin).onTargetsAvailable(emptyList())
verify(plugin).registerSmartspaceEventNotifier(null)
+ verify(weatherPlugin).onTargetsAvailable(emptyList())
+ verify(weatherPlugin).registerSmartspaceEventNotifier(null)
}
@Test
- fun testUserChangeReloadsSmartspace() {
+ fun testUserChange_reloadsSmartspace() {
// GIVEN a connected smartspace session
connectSession()
@@ -308,7 +323,7 @@
}
@Test
- fun testSettingsChangeReloadsSmartspace() {
+ fun testSettingsChange_reloadsSmartspace() {
// GIVEN a connected smartspace session
connectSession()
@@ -320,7 +335,7 @@
}
@Test
- fun testThemeChangeUpdatesTextColor() {
+ fun testThemeChange_updatesTextColor() {
// GIVEN a connected smartspace session
connectSession()
@@ -332,7 +347,22 @@
}
@Test
- fun testDozeAmountChangeUpdatesView() {
+ fun testThemeChange_ifDecouplingEnabled_updatesTextColor() {
+ `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the theme changes
+ configChangeListener.onThemeChanged()
+
+ // We update the new text color to match the wallpaper color
+ verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(smartspaceView).setPrimaryTextColor(anyInt())
+ }
+
+ @Test
+ fun testDozeAmountChange_updatesView() {
// GIVEN a connected smartspace session
connectSession()
@@ -344,7 +374,22 @@
}
@Test
- fun testKeyguardBypassEnabledUpdatesView() {
+ fun testDozeAmountChange_ifDecouplingEnabled_updatesViews() {
+ `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the doze amount changes
+ statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
+
+ // We pass that along to the view
+ verify(weatherSmartspaceView).setDozeAmount(0.7f)
+ verify(smartspaceView).setDozeAmount(0.7f)
+ }
+
+ @Test
+ fun testKeyguardBypassEnabled_updatesView() {
// GIVEN a connected smartspace session
connectSession()
`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -439,6 +484,27 @@
}
@Test
+ fun testSessionListener_ifDecouplingEnabled_weatherTargetIsFilteredOut() {
+ `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandlePrimary),
+ makeTarget(3, userHandleManaged),
+ makeTarget(4, userHandlePrimary, featureType = SmartspaceTarget.FEATURE_WEATHER)
+ )
+
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN all non-sensitive content is still shown
+ verify(plugin).onTargetsAvailable(eq(listOf(targets[0], targets[1], targets[2])))
+ // No filtering is applied for the weather plugin
+ verify(weatherPlugin).onTargetsAvailable(eq(targets))
+ }
+
+ @Test
fun testSettingsAreReloaded() {
// GIVEN a connected session where the privacy settings later flip to false
connectSession()
@@ -529,6 +595,16 @@
}
@Test
+ fun testWeatherViewUsesSameSession() {
+ `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ // GIVEN a connected session
+ connectSession()
+
+ // No checks is needed here, since connectSession() already checks internally that
+ // createSmartspaceSession is invoked only once.
+ }
+
+ @Test
fun testViewGetInitializedWithBypassEnabledState() {
// GIVEN keyguard bypass is enabled.
`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -545,8 +621,8 @@
fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
// GIVEN an uninitalized smartspaceView
// WHEN the device is provisioned
- `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
- `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
deviceProvisionedListener.onDeviceProvisionedChanged()
// THEN no calls to createSmartspaceSession should occur
@@ -556,9 +632,23 @@
}
private fun connectSession() {
+ if (controller.isDateWeatherDecoupled()) {
+ val weatherView = controller.buildAndConnectWeatherView(fakeParent)
+ weatherSmartspaceView = weatherView as SmartspaceView
+ fakeParent.addView(weatherView)
+ controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+
+ verify(weatherSmartspaceView).setUiSurface(
+ BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
+
+ verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(weatherSmartspaceView).setDozeAmount(0.5f)
+ }
+
val view = controller.buildAndConnectView(fakeParent)
smartspaceView = view as SmartspaceView
-
+ fakeParent.addView(view)
controller.stateChangeListener.onViewAttachedToWindow(view)
verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
@@ -568,6 +658,8 @@
.addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
sessionListener = sessionListenerCaptor.value
+ verify(smartspaceManager).createSmartspaceSession(any())
+
verify(userTracker).addCallback(capture(userTrackerCaptor), any())
userListener = userTrackerCaptor.value
@@ -592,9 +684,11 @@
verify(smartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setDozeAmount(0.5f)
- clearInvocations(view)
- fakeParent.addView(view)
+ if (controller.isDateWeatherDecoupled()) {
+ clearInvocations(weatherSmartspaceView)
+ }
+ clearInvocations(smartspaceView)
}
private fun setActiveUser(userHandle: UserHandle) {
@@ -640,6 +734,31 @@
).thenReturn(if (value) 1 else 0)
}
+ // Separate function for the weather view, which doesn't implement all functions in interface.
+ private fun createWeatherSmartspaceView(): SmartspaceView {
+ return spy(object : View(context), SmartspaceView {
+ override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
+ }
+
+ override fun setPrimaryTextColor(color: Int) {
+ }
+
+ override fun setIsDreaming(isDreaming: Boolean) {
+ }
+
+ override fun setUiSurface(uiSurface: String) {
+ }
+
+ override fun setDozeAmount(amount: Float) {
+ }
+
+ override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
+ }
+
+ override fun setFalsingManager(falsingManager: FalsingManager?) {
+ }
+ })
+ }
private fun createSmartspaceView(): SmartspaceView {
return spy(object : View(context), SmartspaceView {
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index be6b1dc..2686238 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -321,7 +321,7 @@
val testDispatcher = UnconfinedTestDispatcher()
val testScope = TestScope(testDispatcher)
val fakeSettings = FakeSettings().apply {
- putBool(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, true)
+ putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
}
val seenNotificationsProvider = SeenNotificationsProviderImpl()
val keyguardCoordinator =
@@ -372,14 +372,14 @@
var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
get() =
- fakeSettings.getBoolForUser(
+ fakeSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
UserHandle.USER_CURRENT,
- )
+ ) == 1
set(value) {
- fakeSettings.putBoolForUser(
+ fakeSettings.putIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- value,
+ if (value) 1 else 2,
UserHandle.USER_CURRENT,
)
}
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 b6a1bb3..d7ac6b4 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
@@ -66,9 +66,9 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -133,18 +133,13 @@
@Mock private ShadeController mShadeController;
@Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@Mock private AssistantFeedbackController mAssistantFeedbackController;
+ @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ @Mock private StatusBarStateController mStatusBarStateController;
@Before
public void setUp() {
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- mDependency.injectTestDependency(DeviceProvisionedController.class,
- mDeviceProvisionedController);
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mDependency.injectTestDependency(
- OnUserInteractionCallback.class,
- mOnUserInteractionCallback);
- mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
@@ -155,7 +150,11 @@
mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
- mShadeController);
+ mShadeController,
+ mNotificationLockscreenUserManager,
+ mStatusBarStateController,
+ mDeviceProvisionedController,
+ mMetricsLogger);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -372,7 +371,8 @@
eq(false),
eq(false),
eq(true), /* wasShownHighPriority */
- eq(mAssistantFeedbackController));
+ eq(mAssistantFeedbackController),
+ any(MetricsLogger.class));
}
@Test
@@ -406,7 +406,8 @@
eq(true),
eq(false),
eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController));
+ eq(mAssistantFeedbackController),
+ any(MetricsLogger.class));
}
@Test
@@ -438,7 +439,8 @@
eq(false),
eq(false),
eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController));
+ eq(mAssistantFeedbackController),
+ any(MetricsLogger.class));
}
////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 80a81a5..8dd0488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -130,7 +130,6 @@
mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -194,7 +193,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -220,7 +220,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkg_icon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -242,7 +243,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -273,7 +275,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -296,7 +299,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
}
@@ -324,7 +328,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -347,7 +352,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -369,7 +375,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -395,7 +402,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -417,7 +425,8 @@
true,
true,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -443,7 +452,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -468,7 +478,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -493,7 +504,8 @@
false,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -515,7 +527,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
@@ -531,7 +544,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -556,7 +570,8 @@
true,
true,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -582,7 +597,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -606,7 +622,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(GONE, mNotificationInfo.findViewById(
R.id.interruptiveness_settings).getVisibility());
assertEquals(VISIBLE, mNotificationInfo.findViewById(
@@ -630,7 +647,8 @@
true,
true,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -673,7 +691,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_call_desc),
@@ -716,7 +735,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(GONE,
mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility());
assertEquals(VISIBLE,
@@ -743,7 +763,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
}
@@ -765,7 +786,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
}
@@ -789,7 +811,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertTrue(mNotificationInfo.findViewById(R.id.automatic).isSelected());
}
@@ -810,7 +833,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected());
}
@@ -831,7 +855,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected());
}
@@ -852,7 +877,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -875,7 +901,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN.getId(),
mUiEventLogger.eventId(0));
@@ -899,7 +926,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mTestableLooper.processAllMessages();
@@ -926,7 +954,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mTestableLooper.processAllMessages();
@@ -953,7 +982,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.automatic).performClick();
mTestableLooper.processAllMessages();
@@ -981,7 +1011,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -1008,7 +1039,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -1043,7 +1075,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.handleCloseControls(true, false);
@@ -1071,7 +1104,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1112,7 +1146,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1149,7 +1184,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.automatic).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1181,7 +1217,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1217,7 +1254,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1255,7 +1293,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.handleCloseControls(false, false);
@@ -1286,7 +1325,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1324,7 +1364,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1353,7 +1394,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1384,7 +1426,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1419,7 +1462,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1452,7 +1496,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1485,7 +1530,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -1511,7 +1557,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertFalse(mNotificationInfo.willBeRemoved());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 091bb54..8cfcc07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -510,6 +510,7 @@
@Test
public void managedProfileAdded_tileAdded() {
when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
+ when(mAutoAddTracker.getRestoredTilePosition(eq("work"))).thenReturn(2);
mAutoTileManager = createAutoTileManager(mContext);
Mockito.doAnswer((Answer<Object>) invocation -> {
mManagedProfileCallback = invocation.getArgument(0);
@@ -520,7 +521,7 @@
mManagedProfileCallback.onManagedProfileChanged();
- verify(mQsTileHost, times(1)).addTile(eq("work"));
+ verify(mQsTileHost, times(1)).addTile(eq("work"), eq(2));
verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index b1363a0..0605398 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -1010,6 +1010,18 @@
}
@Test
+ public void testSetDozingNotUnlocking_transitionToAOD_cancelKeyguardFadingAway() {
+ setDozing(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
+
+ mCentralSurfaces.updateScrimController();
+
+ verify(mScrimController, times(2)).transitionTo(eq(ScrimState.AOD));
+ verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
+ }
+
+ @Test
public void testShowKeyguardImplementation_setsState() {
when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
deleted file mode 100644
index 2f49535..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ /dev/null
@@ -1,526 +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
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Handler;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardSecurityModel;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class KeyguardBouncerTest extends SysuiTestCase {
-
- @Mock
- private FalsingCollector mFalsingCollector;
- @Mock
- private ViewMediatorCallback mViewMediatorCallback;
- @Mock
- private DismissCallbackRegistry mDismissCallbackRegistry;
- @Mock
- private KeyguardHostViewController mKeyguardHostViewController;
- @Mock
- private PrimaryBouncerExpansionCallback mExpansionCallback;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private KeyguardBypassController mKeyguardBypassController;
- @Mock
- private Handler mHandler;
- @Mock
- private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock
- private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
- @Mock
- private KeyguardBouncerComponent mKeyguardBouncerComponent;
- private ViewGroup mContainer;
- @Rule
- public MockitoRule mRule = MockitoJUnit.rule();
- private Integer mRootVisibility = View.INVISIBLE;
- private KeyguardBouncer mBouncer;
-
- @Before
- public void setup() {
- allowTestableLooperAsMainThread();
- when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
- .thenReturn(KeyguardSecurityModel.SecurityMode.None);
- DejankUtils.setImmediate(true);
-
- mContainer = spy(new FrameLayout(getContext()));
- when(mKeyguardBouncerComponentFactory.create(mContainer)).thenReturn(
- mKeyguardBouncerComponent);
- when(mKeyguardBouncerComponent.getKeyguardHostViewController())
- .thenReturn(mKeyguardHostViewController);
-
- mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback,
- mDismissCallbackRegistry, mFalsingCollector,
- mKeyguardStateController, mKeyguardUpdateMonitor,
- mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
- mKeyguardBouncerComponentFactory)
- .create(mContainer, mExpansionCallback);
- }
-
- @Test
- public void testInflateView_doesntCrash() {
- mBouncer.inflateView();
- }
-
- @Test
- public void testShow_notifiesFalsingManager() {
- mBouncer.show(true);
- verify(mFalsingCollector).onBouncerShown();
-
- mBouncer.show(true, false);
- verifyNoMoreInteractions(mFalsingCollector);
- }
-
- /**
- * Regression test: Invisible bouncer when occluded.
- */
- @Test
- public void testShow_bouncerIsVisible() {
- // Expand notification panel as if we were in the keyguard.
- mBouncer.ensureView();
- mBouncer.setExpansion(1);
-
- reset(mKeyguardHostViewController);
-
- mBouncer.show(true);
- verify(mKeyguardHostViewController).setExpansion(0);
- }
-
- @Test
- public void testShow_notifiesVisibility() {
- mBouncer.show(true);
- verify(mKeyguardStateController).notifyBouncerShowing(eq(true));
- verify(mExpansionCallback).onStartingToShow();
-
- // Not called again when visible
- reset(mViewMediatorCallback);
- mBouncer.show(true);
- verifyNoMoreInteractions(mViewMediatorCallback);
- }
-
- @Test
- public void testShow_triesToDismissKeyguard() {
- mBouncer.show(true);
- verify(mKeyguardHostViewController).dismiss(anyInt());
- }
-
- @Test
- public void testShow_resetsSecuritySelection() {
- mBouncer.show(false);
- verify(mKeyguardHostViewController, never()).showPrimarySecurityScreen();
-
- mBouncer.hide(false);
- mBouncer.show(true);
- verify(mKeyguardHostViewController).showPrimarySecurityScreen();
- }
-
- @Test
- public void testShow_animatesKeyguardView() {
- mBouncer.show(true);
- verify(mKeyguardHostViewController).appear(anyInt());
- }
-
- @Test
- public void testShow_showsErrorMessage() {
- final String errorMessage = "an error message";
- when(mViewMediatorCallback.consumeCustomMessage()).thenReturn(errorMessage);
- mBouncer.show(true);
- verify(mKeyguardHostViewController).showErrorMessage(eq(errorMessage));
- }
-
- @Test
- public void testSetExpansion_notifiesFalsingManager() {
- mBouncer.ensureView();
- mBouncer.setExpansion(0.5f);
-
- mBouncer.setExpansion(EXPANSION_HIDDEN);
- verify(mFalsingCollector).onBouncerHidden();
- verify(mExpansionCallback).onFullyHidden();
-
- mBouncer.setExpansion(EXPANSION_VISIBLE);
- verify(mFalsingCollector).onBouncerShown();
- verify(mExpansionCallback).onFullyShown();
-
- verify(mExpansionCallback, never()).onStartingToHide();
- verify(mKeyguardHostViewController, never()).onStartingToHide();
- mBouncer.setExpansion(0.9f);
- verify(mExpansionCallback).onStartingToHide();
- verify(mKeyguardHostViewController).onStartingToHide();
- }
-
- @Test
- public void testSetExpansion_notifiesKeyguardView() {
- mBouncer.ensureView();
- mBouncer.setExpansion(0.1f);
-
- mBouncer.setExpansion(0);
- verify(mKeyguardHostViewController).onResume();
- verify(mContainer).announceForAccessibility(any());
- }
-
- @Test
- public void show_notifiesKeyguardViewController() {
- mBouncer.ensureView();
-
- mBouncer.show(/* resetSecuritySelection= */ false);
-
- verify(mKeyguardHostViewController).onBouncerVisibilityChanged(View.VISIBLE);
- }
-
- @Test
- public void testHide_notifiesFalsingManager() {
- mBouncer.hide(false);
- verify(mFalsingCollector).onBouncerHidden();
- }
-
- @Test
- public void testHide_notifiesVisibility() {
- mBouncer.hide(false);
- verify(mKeyguardStateController).notifyBouncerShowing(eq(false));
- }
-
- @Test
- public void testHide_notifiesDismissCallbackIfVisible() {
- mBouncer.hide(false);
- verifyZeroInteractions(mDismissCallbackRegistry);
- mBouncer.show(false);
- mBouncer.hide(false);
- verify(mDismissCallbackRegistry).notifyDismissCancelled();
- }
-
- @Test
- public void testHide_notShowingAnymore() {
- mBouncer.ensureView();
- mBouncer.show(false /* resetSecuritySelection */);
- mBouncer.hide(false /* destroyViews */);
- Assert.assertFalse("Not showing", mBouncer.isShowing());
- }
-
- @Test
- public void testShowPromptReason_propagates() {
- mBouncer.ensureView();
- mBouncer.showPromptReason(1);
- verify(mKeyguardHostViewController).showPromptReason(eq(1));
- }
-
- @Test
- public void testShowMessage_propagates() {
- final String message = "a message";
- mBouncer.ensureView();
- mBouncer.showMessage(message, ColorStateList.valueOf(Color.GREEN));
- verify(mKeyguardHostViewController).showMessage(
- eq(message), eq(ColorStateList.valueOf(Color.GREEN)));
- }
-
- @Test
- public void testShowOnDismissAction_showsBouncer() {
- final OnDismissAction dismissAction = () -> false;
- final Runnable cancelAction = () -> {};
- mBouncer.showWithDismissAction(dismissAction, cancelAction);
- verify(mKeyguardHostViewController).setOnDismissAction(dismissAction, cancelAction);
- Assert.assertTrue("Should be showing", mBouncer.isShowing());
- }
-
- @Test
- public void testStartPreHideAnimation_notifiesView() {
- final boolean[] ran = {false};
- final Runnable r = () -> ran[0] = true;
- mBouncer.startPreHideAnimation(r);
- Assert.assertTrue("Callback should have been invoked", ran[0]);
-
- ran[0] = false;
- mBouncer.ensureView();
- mBouncer.startPreHideAnimation(r);
- verify(mKeyguardHostViewController).startDisappearAnimation(r);
- Assert.assertFalse("Callback should have been deferred", ran[0]);
- }
-
- @Test
- public void testIsShowing_animated() {
- Assert.assertFalse("Show wasn't invoked yet", mBouncer.isShowing());
- mBouncer.show(true /* reset */);
- Assert.assertTrue("Should be showing", mBouncer.isShowing());
- }
-
- @Test
- public void testIsShowing_forSwipeUp() {
- mBouncer.setExpansion(1f);
- mBouncer.show(true /* reset */, false /* animated */);
- Assert.assertFalse("Should only be showing after collapsing notification panel",
- mBouncer.isShowing());
- mBouncer.setExpansion(0f);
- Assert.assertTrue("Should be showing", mBouncer.isShowing());
- }
-
- @Test
- public void testSetExpansion() {
- mBouncer.ensureView();
- mBouncer.setExpansion(0.5f);
- verify(mKeyguardHostViewController).setExpansion(0.5f);
- }
-
- @Test
- public void testIsFullscreenBouncer_asksKeyguardView() {
- mBouncer.ensureView();
- mBouncer.isFullscreenBouncer();
- verify(mKeyguardHostViewController).getCurrentSecurityMode();
- }
-
- @Test
- public void testIsHiding_preHideOrHide() {
- Assert.assertFalse("Should not be hiding on initial state", mBouncer.isAnimatingAway());
- mBouncer.startPreHideAnimation(null /* runnable */);
- Assert.assertTrue("Should be hiding during pre-hide", mBouncer.isAnimatingAway());
- mBouncer.hide(false /* destroyView */);
- Assert.assertFalse("Should be hidden after hide()", mBouncer.isAnimatingAway());
- }
-
- @Test
- public void testIsHiding_skipsTranslation() {
- mBouncer.show(false /* reset */);
- reset(mKeyguardHostViewController);
- mBouncer.startPreHideAnimation(null /* runnable */);
- mBouncer.setExpansion(0.5f);
- verify(mKeyguardHostViewController, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void testIsSecure() {
- mBouncer.ensureView();
- for (KeyguardSecurityModel.SecurityMode mode : KeyguardSecurityModel.SecurityMode.values()){
- reset(mKeyguardSecurityModel);
- when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(mode);
- Assert.assertEquals("Security doesn't match for mode: " + mode,
- mBouncer.isSecure(), mode != KeyguardSecurityModel.SecurityMode.None);
- }
- }
-
- @Test
- public void testIsShowingScrimmed_true() {
- doAnswer(invocation -> {
- assertThat(mBouncer.isScrimmed()).isTrue();
- return null;
- }).when(mExpansionCallback).onFullyShown();
- mBouncer.show(false /* resetSecuritySelection */, true /* animate */);
- assertThat(mBouncer.isScrimmed()).isTrue();
- mBouncer.hide(false /* destroyView */);
- assertThat(mBouncer.isScrimmed()).isFalse();
- }
-
- @Test
- public void testIsShowingScrimmed_false() {
- doAnswer(invocation -> {
- assertThat(mBouncer.isScrimmed()).isFalse();
- return null;
- }).when(mExpansionCallback).onFullyShown();
- mBouncer.show(false /* resetSecuritySelection */, false /* animate */);
- assertThat(mBouncer.isScrimmed()).isFalse();
- }
-
- @Test
- public void testWillDismissWithAction() {
- mBouncer.ensureView();
- Assert.assertFalse("Action not set yet", mBouncer.willDismissWithAction());
- when(mKeyguardHostViewController.hasDismissActions()).thenReturn(true);
- Assert.assertTrue("Action should exist", mBouncer.willDismissWithAction());
- }
-
- @Test
- public void testShow_delaysIfFaceAuthIsRunning() {
- when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
- .thenReturn(true);
- when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- mBouncer.show(true /* reset */);
-
- ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class);
- verify(mHandler).postDelayed(showRunnable.capture(),
- eq(KeyguardBouncer.BOUNCER_FACE_DELAY));
-
- mBouncer.hide(false /* destroyView */);
- verify(mHandler).removeCallbacks(eq(showRunnable.getValue()));
- }
-
- @Test
- public void testShow_doesNotDelaysIfFaceAuthIsNotAllowed() {
- when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
- .thenReturn(false);
- mBouncer.show(true /* reset */);
-
- verify(mHandler, never()).postDelayed(any(), anyLong());
- }
-
- @Test
- public void testShow_delaysIfFaceAuthIsRunning_unlessBypassEnabled() {
- when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- mBouncer.show(true /* reset */);
-
- verify(mHandler, never()).postDelayed(any(), anyLong());
- }
-
- @Test
- public void testShow_delaysIfFaceAuthIsRunning_unlessFingerprintEnrolled() {
- when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0))
- .thenReturn(true);
- mBouncer.show(true /* reset */);
-
- verify(mHandler, never()).postDelayed(any(), anyLong());
- }
-
- @Test
- public void testRegisterUpdateMonitorCallback() {
- verify(mKeyguardUpdateMonitor).registerCallback(any());
- }
-
- @Test
- public void testInTransit_whenTranslation() {
- mBouncer.show(true);
- mBouncer.setExpansion(EXPANSION_HIDDEN);
- assertThat(mBouncer.inTransit()).isFalse();
- mBouncer.setExpansion(0.5f);
- assertThat(mBouncer.inTransit()).isTrue();
- mBouncer.setExpansion(EXPANSION_VISIBLE);
- assertThat(mBouncer.inTransit()).isFalse();
- }
-
- @Test
- public void testUpdateResources_delegatesToRootView() {
- mBouncer.ensureView();
- mBouncer.updateResources();
-
- // This is mocked, so won't pick up on the call to updateResources via
- // mKeyguardViewController.init(), only updateResources above.
- verify(mKeyguardHostViewController).updateResources();
- }
-
- @Test
- public void testUpdateKeyguardPosition_delegatesToRootView() {
- mBouncer.ensureView();
- mBouncer.updateKeyguardPosition(1.0f);
-
- verify(mKeyguardHostViewController).updateKeyguardPosition(1.0f);
- }
-
- @Test
- public void testExpansion_notifiesCallback() {
- mBouncer.ensureView();
- mBouncer.setExpansion(0.5f);
-
- final PrimaryBouncerExpansionCallback callback =
- mock(PrimaryBouncerExpansionCallback.class);
- mBouncer.addBouncerExpansionCallback(callback);
-
- mBouncer.setExpansion(EXPANSION_HIDDEN);
- verify(callback).onFullyHidden();
- verify(callback).onExpansionChanged(EXPANSION_HIDDEN);
-
- Mockito.clearInvocations(callback);
- mBouncer.setExpansion(EXPANSION_VISIBLE);
- verify(callback).onFullyShown();
- verify(callback).onExpansionChanged(EXPANSION_VISIBLE);
-
- Mockito.clearInvocations(callback);
- float bouncerHideAmount = 0.9f;
- // Ensure the callback only triggers once despite multiple calls to setExpansion
- // with the same value.
- mBouncer.setExpansion(bouncerHideAmount);
- mBouncer.setExpansion(bouncerHideAmount);
- verify(callback, times(1)).onStartingToHide();
- verify(callback, times(1)).onExpansionChanged(bouncerHideAmount);
-
- Mockito.clearInvocations(callback);
- mBouncer.removeBouncerExpansionCallback(callback);
- bouncerHideAmount = 0.5f;
- mBouncer.setExpansion(bouncerHideAmount);
- verify(callback, never()).onExpansionChanged(bouncerHideAmount);
- }
-
- @Test
- public void testOnResumeCalledForFullscreenBouncerOnSecondShow() {
- // GIVEN a security mode which requires fullscreen bouncer
- when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
- .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
- mBouncer.show(true);
-
- // WHEN a second call to show occurs, the bouncer will already by visible
- reset(mKeyguardHostViewController);
- mBouncer.show(true);
-
- // THEN ensure the ViewController is told to resume
- verify(mKeyguardHostViewController).onResume();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index cc4abfc..529519a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import org.junit.Before;
@@ -67,7 +68,8 @@
mStatusBarIconController,
mock(BatteryController.class),
mock(NavigationModeController.class),
- mock(DumpManager.class));
+ mock(DumpManager.class),
+ new FakeDisplayTracker(mContext));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
index 189aa0f..0a68406 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -28,6 +28,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.LightBarTransitionsController.DarkIntensityApplier;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -56,7 +57,8 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mLightBarTransitionsController = new LightBarTransitionsController(mContext, mApplier,
- new CommandQueue(mContext), mKeyguardStateController, mStatusBarStateController);
+ new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
+ mKeyguardStateController, mStatusBarStateController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1ba0a36..d8446f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.flags.Flags.MODERN_BOUNCER;
import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
@@ -109,7 +108,6 @@
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private View mNotificationContainer;
@Mock private KeyguardBypassController mBypassController;
- @Mock private KeyguardBouncer.Factory mKeyguardBouncerFactory;
@Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
@Mock private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock private KeyguardMessageArea mKeyguardMessageArea;
@@ -153,8 +151,6 @@
.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM))
.thenReturn(true);
- when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(true);
-
mStatusBarKeyguardViewManager =
new StatusBarKeyguardViewManager(
getContext(),
@@ -169,7 +165,6 @@
mock(NotificationShadeWindowController.class),
mKeyguardStateController,
mock(NotificationMediaManager.class),
- mKeyguardBouncerFactory,
mKeyguardMessageAreaFactory,
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
@@ -660,7 +655,6 @@
mock(NotificationShadeWindowController.class),
mKeyguardStateController,
mock(NotificationMediaManager.class),
- mKeyguardBouncerFactory,
mKeyguardMessageAreaFactory,
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java
deleted file mode 100644
index 55ab681..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.flags.Flags.MODERN_BOUNCER;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewRootImpl;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
-import android.window.WindowOnBackInvokedDispatcher;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardMessageArea;
-import com.android.keyguard.KeyguardMessageAreaController;
-import com.android.keyguard.KeyguardSecurityModel;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyguard.data.BouncerView;
-import com.android.systemui.keyguard.data.BouncerViewDelegate;
-import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.shade.NotificationPanelViewController;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
-
-import com.google.common.truth.Truth;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/**
- * StatusBarKeyguardViewManager Test with deprecated KeyguardBouncer.java.
- * TODO: Delete when deleting {@link KeyguardBouncer}
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
- private static final ShadeExpansionChangeEvent EXPANSION_EVENT =
- expansionEvent(/* fraction= */ 0.5f, /* expanded= */ false, /* tracking= */ true);
-
- @Mock private ViewMediatorCallback mViewMediatorCallback;
- @Mock private LockPatternUtils mLockPatternUtils;
- @Mock private CentralSurfaces mCentralSurfaces;
- @Mock private ViewGroup mContainer;
- @Mock private NotificationPanelViewController mNotificationPanelView;
- @Mock private BiometricUnlockController mBiometricUnlockController;
- @Mock private SysuiStatusBarStateController mStatusBarStateController;
- @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock private View mNotificationContainer;
- @Mock private KeyguardBypassController mBypassController;
- @Mock private KeyguardBouncer.Factory mKeyguardBouncerFactory;
- @Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
- @Mock private KeyguardMessageAreaController mKeyguardMessageAreaController;
- @Mock private KeyguardBouncer mPrimaryBouncer;
- @Mock private KeyguardMessageArea mKeyguardMessageArea;
- @Mock private ShadeController mShadeController;
- @Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
- @Mock private DreamOverlayStateController mDreamOverlayStateController;
- @Mock private LatencyTracker mLatencyTracker;
- @Mock private FeatureFlags mFeatureFlags;
- @Mock private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
- @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
- @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
- @Mock private BouncerView mBouncerView;
- @Mock private BouncerViewDelegate mBouncerViewDelegate;
-
- private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
- mBouncerExpansionCallback;
- private FakeKeyguardStateController mKeyguardStateController =
- spy(new FakeKeyguardStateController());
-
- @Mock private ViewRootImpl mViewRootImpl;
- @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
- @Captor
- private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
-
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mKeyguardBouncerFactory.create(
- any(ViewGroup.class),
- any(PrimaryBouncerExpansionCallback.class)))
- .thenReturn(mPrimaryBouncer);
- when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
- when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
- when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
- .thenReturn(mKeyguardMessageAreaController);
- when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
-
- mStatusBarKeyguardViewManager =
- new StatusBarKeyguardViewManager(
- getContext(),
- mViewMediatorCallback,
- mLockPatternUtils,
- mStatusBarStateController,
- mock(ConfigurationController.class),
- mKeyguardUpdateMonitor,
- mDreamOverlayStateController,
- mock(NavigationModeController.class),
- mock(DockManager.class),
- mock(NotificationShadeWindowController.class),
- mKeyguardStateController,
- mock(NotificationMediaManager.class),
- mKeyguardBouncerFactory,
- mKeyguardMessageAreaFactory,
- Optional.of(mSysUiUnfoldComponent),
- () -> mShadeController,
- mLatencyTracker,
- mKeyguardSecurityModel,
- mFeatureFlags,
- mPrimaryBouncerCallbackInteractor,
- mPrimaryBouncerInteractor,
- mBouncerView,
- mAlternateBouncerInteractor) {
- @Override
- public ViewRootImpl getViewRootImpl() {
- return mViewRootImpl;
- }
- };
- when(mViewRootImpl.getOnBackInvokedDispatcher())
- .thenReturn(mOnBackInvokedDispatcher);
- mStatusBarKeyguardViewManager.registerCentralSurfaces(
- mCentralSurfaces,
- mNotificationPanelView,
- new ShadeExpansionStateManager(),
- mBiometricUnlockController,
- mNotificationContainer,
- mBypassController);
- mStatusBarKeyguardViewManager.show(null);
- ArgumentCaptor<PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
- ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
- verify(mKeyguardBouncerFactory).create(any(ViewGroup.class),
- callbackArgumentCaptor.capture());
- mBouncerExpansionCallback = callbackArgumentCaptor.getValue();
- }
-
- @Test
- public void dismissWithAction_AfterKeyguardGoneSetToFalse() {
- OnDismissAction action = () -> false;
- Runnable cancelAction = () -> {};
- mStatusBarKeyguardViewManager.dismissWithAction(
- action, cancelAction, false /* afterKeyguardGone */);
- verify(mPrimaryBouncer).showWithDismissAction(eq(action), eq(cancelAction));
- }
-
- @Test
- public void showBouncer_onlyWhenShowing() {
- mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
- verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
- verify(mPrimaryBouncer, never()).show(anyBoolean());
- }
-
- @Test
- public void showBouncer_notWhenBouncerAlreadyShowing() {
- mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- when(mPrimaryBouncer.isSecure()).thenReturn(true);
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
- verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
- verify(mPrimaryBouncer, never()).show(anyBoolean());
- }
-
- @Test
- public void showBouncer_showsTheBouncer() {
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
- verify(mPrimaryBouncer).show(anyBoolean(), eq(true));
- }
-
- @Test
- public void onPanelExpansionChanged_neverShowsDuringHintAnimation() {
- when(mNotificationPanelView.isUnlockHintRunning()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void onPanelExpansionChanged_propagatesToBouncerOnlyIfShowing() {
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer, never()).setExpansion(eq(0.5f));
-
- when(mPrimaryBouncer.isShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- expansionEvent(/* fraction= */ 0.6f, /* expanded= */ false, /* tracking= */ true));
- verify(mPrimaryBouncer).setExpansion(eq(0.6f));
- }
-
- @Test
- public void onPanelExpansionChanged_duplicateEventsAreIgnored() {
- when(mPrimaryBouncer.isShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer).setExpansion(eq(0.5f));
-
- reset(mPrimaryBouncer);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer, never()).setExpansion(eq(0.5f));
- }
-
- @Test
- public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
- mStatusBarKeyguardViewManager.hide(0, 0);
- when(mPrimaryBouncer.inTransit()).thenReturn(true);
-
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer).setExpansion(eq(EXPANSION_HIDDEN));
- }
-
- @Test
- public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
- mKeyguardStateController.setCanDismissLockScreen(false);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer).show(eq(false), eq(false));
-
- // But not when it's already visible
- reset(mPrimaryBouncer);
- when(mPrimaryBouncer.isShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
-
- // Or animating away
- reset(mPrimaryBouncer);
- when(mPrimaryBouncer.isAnimatingAway()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
- when(mBiometricUnlockController.getMode())
- .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- expansionEvent(
- /* fraction= */ EXPANSION_VISIBLE,
- /* expanded= */ true,
- /* tracking= */ false));
- verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenDismissBouncer() {
- // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
- // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
- // which would mistakenly cause the bouncer to show briefly before its visibility
- // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
- // bouncer if the bouncer is dismissing as a result of a biometric unlock.
- when(mBiometricUnlockController.getMode())
- .thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- expansionEvent(
- /* fraction= */ EXPANSION_VISIBLE,
- /* expanded= */ true,
- /* tracking= */ false));
- verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenOccluded() {
- when(mKeyguardStateController.isOccluded()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- expansionEvent(
- /* fraction= */ EXPANSION_VISIBLE,
- /* expanded= */ true,
- /* tracking= */ false));
- verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenShowBouncer() {
- // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
- // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
- // which would mistakenly cause the bouncer to show briefly before its visibility
- // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
- // bouncer if the bouncer is dismissing as a result of a biometric unlock.
- when(mBiometricUnlockController.getMode())
- .thenReturn(BiometricUnlockController.MODE_SHOW_BOUNCER);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- expansionEvent(
- /* fraction= */ EXPANSION_VISIBLE,
- /* expanded= */ true,
- /* tracking= */ false));
- verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() {
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- expansionEvent(
- /* fraction= */ EXPANSION_VISIBLE,
- /* expanded= */ true,
- /* tracking= */ false));
- verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
- }
-
- @Test
- public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
- mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mCentralSurfaces).animateKeyguardUnoccluding();
-
- when(mPrimaryBouncer.isShowing()).thenReturn(true);
- clearInvocations(mCentralSurfaces);
- mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mCentralSurfaces, never()).animateKeyguardUnoccluding();
- }
-
- @Test
- public void setOccluded_onKeyguardOccludedChangedCalled() {
- clearInvocations(mKeyguardStateController);
- clearInvocations(mKeyguardUpdateMonitor);
-
- mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
- verify(mKeyguardStateController).notifyKeyguardState(true, false);
-
- clearInvocations(mKeyguardUpdateMonitor);
- clearInvocations(mKeyguardStateController);
-
- mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardStateController).notifyKeyguardState(true, true);
-
- clearInvocations(mKeyguardUpdateMonitor);
- clearInvocations(mKeyguardStateController);
-
- mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
- verify(mKeyguardStateController).notifyKeyguardState(true, false);
- }
-
- @Test
- public void setOccluded_isInLaunchTransition_onKeyguardOccludedChangedCalled() {
- mStatusBarKeyguardViewManager.show(null);
-
- mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardStateController).notifyKeyguardState(true, true);
- }
-
- @Test
- public void setOccluded_isLaunchingActivityOverLockscreen_onKeyguardOccludedChangedCalled() {
- when(mCentralSurfaces.isLaunchingActivityOverLockscreen()).thenReturn(true);
- mStatusBarKeyguardViewManager.show(null);
-
- mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardStateController).notifyKeyguardState(true, true);
- }
-
- @Test
- public void testHiding_cancelsGoneRunnable() {
- OnDismissAction action = mock(OnDismissAction.class);
- Runnable cancelAction = mock(Runnable.class);
- mStatusBarKeyguardViewManager.dismissWithAction(
- action, cancelAction, true /* afterKeyguardGone */);
-
- when(mPrimaryBouncer.isShowing()).thenReturn(false);
- mStatusBarKeyguardViewManager.hideBouncer(true);
- mStatusBarKeyguardViewManager.hide(0, 30);
- verify(action, never()).onDismiss();
- verify(cancelAction).run();
- }
-
- @Test
- public void testHidingBouncer_cancelsGoneRunnable() {
- OnDismissAction action = mock(OnDismissAction.class);
- Runnable cancelAction = mock(Runnable.class);
- mStatusBarKeyguardViewManager.dismissWithAction(
- action, cancelAction, true /* afterKeyguardGone */);
-
- when(mPrimaryBouncer.isShowing()).thenReturn(false);
- mStatusBarKeyguardViewManager.hideBouncer(true);
-
- verify(action, never()).onDismiss();
- verify(cancelAction).run();
- }
-
- @Test
- public void testHiding_doesntCancelWhenShowing() {
- OnDismissAction action = mock(OnDismissAction.class);
- Runnable cancelAction = mock(Runnable.class);
- mStatusBarKeyguardViewManager.dismissWithAction(
- action, cancelAction, true /* afterKeyguardGone */);
-
- mStatusBarKeyguardViewManager.hide(0, 30);
- verify(action).onDismiss();
- verify(cancelAction, never()).run();
- }
-
- @Test
- public void testBouncerIsOrWillBeShowing_whenBouncerIsInTransit() {
- when(mPrimaryBouncer.isShowing()).thenReturn(false);
- when(mPrimaryBouncer.inTransit()).thenReturn(true);
-
- assertTrue(
- "Is or will be showing should be true when bouncer is in transit",
- mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing());
- }
-
- @Test
- public void testUpdateResources_delegatesToBouncer() {
- mStatusBarKeyguardViewManager.updateResources();
-
- verify(mPrimaryBouncer).updateResources();
- }
-
- @Test
- public void updateKeyguardPosition_delegatesToBouncer() {
- mStatusBarKeyguardViewManager.updateKeyguardPosition(1.0f);
-
- verify(mPrimaryBouncer).updateKeyguardPosition(1.0f);
- }
-
- @Test
- public void testIsBouncerInTransit() {
- when(mPrimaryBouncer.inTransit()).thenReturn(true);
- Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isTrue();
- when(mPrimaryBouncer.inTransit()).thenReturn(false);
- Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
- mPrimaryBouncer = null;
- Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
- }
-
- private static ShadeExpansionChangeEvent expansionEvent(
- float fraction, boolean expanded, boolean tracking) {
- return new ShadeExpansionChangeEvent(
- fraction, expanded, tracking, /* dragDownPxAmount= */ 0f);
- }
-
- @Test
- public void testPredictiveBackCallback_registration() {
- /* verify that a predictive back callback is registered when the bouncer becomes visible */
- mBouncerExpansionCallback.onVisibilityChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
- mOnBackInvokedCallback.capture());
-
- /* verify that the same callback is unregistered when the bouncer becomes invisible */
- mBouncerExpansionCallback.onVisibilityChanged(false);
- verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
- eq(mOnBackInvokedCallback.getValue()));
- }
-
- @Test
- public void testPredictiveBackCallback_invocationHidesBouncer() {
- mBouncerExpansionCallback.onVisibilityChanged(true);
- /* capture the predictive back callback during registration */
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
- mOnBackInvokedCallback.capture());
-
- when(mPrimaryBouncer.isShowing()).thenReturn(true);
- when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true);
- /* invoke the back callback directly */
- mOnBackInvokedCallback.getValue().onBackInvoked();
-
- /* verify that the bouncer will be hidden as a result of the invocation */
- verify(mCentralSurfaces).setBouncerShowing(eq(false));
- }
-
- @Test
- public void testReportBouncerOnDreamWhenVisible() {
- mBouncerExpansionCallback.onVisibilityChanged(true);
- verify(mCentralSurfaces).setBouncerShowingOverDream(false);
- Mockito.clearInvocations(mCentralSurfaces);
- when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
- mBouncerExpansionCallback.onVisibilityChanged(true);
- verify(mCentralSurfaces).setBouncerShowingOverDream(true);
- }
-
- @Test
- public void testReportBouncerOnDreamWhenNotVisible() {
- mBouncerExpansionCallback.onVisibilityChanged(false);
- verify(mCentralSurfaces).setBouncerShowingOverDream(false);
- Mockito.clearInvocations(mCentralSurfaces);
- when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
- mBouncerExpansionCallback.onVisibilityChanged(false);
- verify(mCentralSurfaces).setBouncerShowingOverDream(false);
- }
-
- @Test
- public void flag_off_DoesNotCallBouncerInteractor() {
- when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(false);
- mStatusBarKeyguardViewManager.hideBouncer(false);
- verify(mPrimaryBouncerInteractor, never()).hide();
- }
-
- @Test
- public void hideAlternateBouncer_beforeCentralSurfacesRegistered() {
- mStatusBarKeyguardViewManager =
- new StatusBarKeyguardViewManager(
- getContext(),
- mViewMediatorCallback,
- mLockPatternUtils,
- mStatusBarStateController,
- mock(ConfigurationController.class),
- mKeyguardUpdateMonitor,
- mDreamOverlayStateController,
- mock(NavigationModeController.class),
- mock(DockManager.class),
- mock(NotificationShadeWindowController.class),
- mKeyguardStateController,
- mock(NotificationMediaManager.class),
- mKeyguardBouncerFactory,
- mKeyguardMessageAreaFactory,
- Optional.of(mSysUiUnfoldComponent),
- () -> mShadeController,
- mLatencyTracker,
- mKeyguardSecurityModel,
- mFeatureFlags,
- mPrimaryBouncerCallbackInteractor,
- mPrimaryBouncerInteractor,
- mBouncerView,
- mAlternateBouncerInteractor) {
- @Override
- public ViewRootImpl getViewRootImpl() {
- return mViewRootImpl;
- }
- };
-
- // the following call before registering centralSurfaces should NOT throw a NPE:
- mStatusBarKeyguardViewManager.hideAlternateBouncer(true);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index eef43bd..8841521 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeController;
@@ -92,7 +93,7 @@
mMetricsLogger = new FakeMetricsLogger();
LockscreenGestureLogger lockscreenGestureLogger = new LockscreenGestureLogger(
mMetricsLogger);
- mCommandQueue = new CommandQueue(mContext);
+ mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
mDependency.injectTestDependency(StatusBarStateController.class,
mock(SysuiStatusBarStateController.class));
mDependency.injectTestDependency(ShadeController.class, mShadeController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 613238f..929099a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -32,6 +32,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -78,7 +79,8 @@
mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
mock(GroupExpansionManager.class), mNotificationLockscreenUserManager,
mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
- mActivityStarter, mShadeController, new CommandQueue(mContext),
+ mActivityStarter, mShadeController,
+ new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
mock(ActionClickLogger.class), mFakeExecutor));
mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index ae390a0..db8172a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -22,6 +22,7 @@
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.os.ParcelUuid
import android.provider.Settings
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionInfo
@@ -50,6 +51,7 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
+import java.util.UUID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -104,6 +106,17 @@
mock<TableLogBuffer>()
}
+ // For convenience, set up the subscription info callbacks
+ whenever(subscriptionManager.getActiveSubscriptionInfo(anyInt())).thenAnswer { invocation ->
+ when (invocation.getArgument(0) as Int) {
+ 1 -> SUB_1
+ 2 -> SUB_2
+ 3 -> SUB_3
+ 4 -> SUB_4
+ else -> null
+ }
+ }
+
wifiRepository = FakeWifiRepository()
connectionFactory =
@@ -686,6 +699,38 @@
job.cancel()
}
+ @Test
+ fun `active data change - in same group - emits unit`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Unit? = null
+ val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_4_ID_GROUPED)
+
+ assertThat(latest).isEqualTo(Unit)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `active data change - not in same group - does not emit`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Unit? = null
+ val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_1_ID)
+
+ assertThat(latest).isEqualTo(null)
+
+ job.cancel()
+ }
+
private fun createCapabilities(connected: Boolean, validated: Boolean): NetworkCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(connected)
@@ -719,19 +764,50 @@
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
+
+ // Subscription 1
private const val SUB_1_ID = 1
private val SUB_1 =
- mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_1_ID)
+ whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ }
private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
+ // Subscription 2
private const val SUB_2_ID = 2
private val SUB_2 =
- mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_2_ID)
+ whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ }
private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+ // Subs 3 and 4 are considered to be in the same group ------------------------------------
+ private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID())
+
+ // Subscription 3
+ private const val SUB_3_ID_GROUPED = 3
+ private val SUB_3 =
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED)
+ whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+ }
+
+ // Subscription 4
+ private const val SUB_4_ID_GROUPED = 4
+ private val SUB_4 =
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED)
+ whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+ }
+
+ // Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
private const val NET_ID = 123
private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
+ // Carrier merged subscription
private const val SUB_CM_ID = 5
private val SUB_CM =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
index b32058f..3dccbbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
@@ -45,7 +45,7 @@
@Test
fun testLogNetworkCapsChange_bufferHasInfo() {
- logger.logOnCapabilitiesChanged(NET_1, NET_1_CAPS)
+ logger.logOnCapabilitiesChanged(NET_1, NET_1_CAPS, isDefaultNetworkCallback = true)
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
@@ -54,6 +54,7 @@
val expectedNetId = NET_1_ID.toString()
val expectedCaps = NET_1_CAPS.toString()
+ assertThat(actualString).contains("true")
assertThat(actualString).contains(expectedNetId)
assertThat(actualString).contains(expectedCaps)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 87ce8fa..7099f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -21,7 +21,10 @@
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.TransportInfo
+import android.net.VpnTransportInfo
import android.net.vcn.VcnTransportInfo
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
@@ -243,6 +246,54 @@
job.cancel()
}
+ /** Regression test for b/266628069. */
+ @Test
+ fun isWifiDefault_transportInfoIsNotWifi_andNoWifiTransport_false() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
+
+ val transportInfo = VpnTransportInfo(
+ /* type= */ 0,
+ /* sessionId= */ "sessionId",
+ )
+ val networkCapabilities = mock<NetworkCapabilities>().also {
+ whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
+ whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+ whenever(it.transportInfo).thenReturn(transportInfo)
+ }
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, networkCapabilities)
+
+ assertThat(underTest.isWifiDefault.value).isFalse()
+
+ job.cancel()
+ }
+
+ /** Regression test for b/266628069. */
+ @Test
+ fun isWifiDefault_transportInfoIsNotWifi_butHasWifiTransport_true() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
+
+ val transportInfo = VpnTransportInfo(
+ /* type= */ 0,
+ /* sessionId= */ "sessionId",
+ )
+ val networkCapabilities = mock<NetworkCapabilities>().also {
+ whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+ whenever(it.transportInfo).thenReturn(transportInfo)
+ }
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, networkCapabilities)
+
+ assertThat(underTest.isWifiDefault.value).isTrue()
+
+ job.cancel()
+ }
+
@Test
fun isWifiDefault_cellularVcnNetwork_isTrue() = runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this)
@@ -260,6 +311,24 @@
}
@Test
+ fun wifiNetwork_cellularAndWifiTransports_usesCellular_isTrue() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
+
+ val capabilities = mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+ }
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(underTest.isWifiDefault.value).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
fun isWifiDefault_cellularNotVcnNetwork_isFalse() = runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this)
@@ -467,6 +536,28 @@
job.cancel()
}
+ /** Regression test for b/266628069. */
+ @Test
+ fun wifiNetwork_transportInfoIsNotWifi_flowHasNoNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val transportInfo = VpnTransportInfo(
+ /* type= */ 0,
+ /* sessionId= */ "sessionId",
+ )
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(transportInfo))
+
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
@Test
fun wifiNetwork_cellularVcnNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
@@ -535,6 +626,31 @@
}
@Test
+ fun wifiNetwork_cellularAndWifiTransports_usesCellular() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val capabilities = mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
+ }
+
+ @Test
fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() = runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
val job = underTest
@@ -870,12 +986,12 @@
}
private fun createWifiNetworkCapabilities(
- wifiInfo: WifiInfo,
+ transportInfo: TransportInfo,
isValidated: Boolean = true,
): NetworkCapabilities {
return mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
- whenever(it.transportInfo).thenReturn(wifiInfo)
+ whenever(it.transportInfo).thenReturn(transportInfo)
whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(isValidated)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
index 1cccd65c8..cc6be5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
@@ -92,6 +92,20 @@
}
@Test
+ fun onStylusAdded_internal_updatesNotificationSuppression() {
+ startable.onStylusAdded(STYLUS_DEVICE_ID)
+
+ verify(stylusUsiPowerUi, times(1)).updateSuppression(false)
+ }
+
+ @Test
+ fun onStylusAdded_external_noop() {
+ startable.onStylusAdded(EXTERNAL_DEVICE_ID)
+
+ verifyZeroInteractions(stylusUsiPowerUi)
+ }
+
+ @Test
fun onStylusBluetoothConnected_refreshesNotification() {
startable.onStylusBluetoothConnected(STYLUS_DEVICE_ID, "ANY")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
index 1e81dc7..e1668e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
@@ -36,7 +36,6 @@
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -79,7 +78,7 @@
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf())
whenever(inputManager.getInputDevice(0)).thenReturn(btStylusDevice)
whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
- // whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
+ whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
stylusUsiPowerUi = StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler)
broadcastReceiver = stylusUsiPowerUi.receiver
@@ -179,7 +178,6 @@
}
@Test
- @Ignore("TODO(b/257936830): get bt address once input api available")
fun refresh_hasConnectedBluetoothStylus_cancelsNotification() {
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
@@ -189,7 +187,6 @@
}
@Test
- @Ignore("TODO(b/257936830): get bt address once input api available")
fun refresh_hasConnectedBluetoothStylus_existingNotification_cancelsNotification() {
stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 45f7df3..c7c6b94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -1172,7 +1172,7 @@
inner class Listener : TemporaryViewDisplayController.Listener {
val permanentlyRemovedIds = mutableListOf<String>()
- override fun onInfoPermanentlyRemoved(id: String) {
+ override fun onInfoPermanentlyRemoved(id: String, reason: String) {
permanentlyRemovedIds.add(id)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 45eb1f9..dd04ac4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -66,7 +66,7 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class ChipbarCoordinatorTest : SysuiTestCase() {
- private lateinit var underTest: FakeChipbarCoordinator
+ private lateinit var underTest: ChipbarCoordinator
@Mock private lateinit var logger: ChipbarLogger
@Mock private lateinit var accessibilityManager: AccessibilityManager
@@ -100,7 +100,7 @@
uiEventLoggerFake = UiEventLoggerFake()
underTest =
- FakeChipbarCoordinator(
+ ChipbarCoordinator(
context,
logger,
windowManager,
@@ -436,6 +436,23 @@
verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
}
+ /** Regression test for b/266209420. */
+ @Test
+ fun displayViewThenImmediateRemoval_viewStillRemoved() {
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+ Text.Loaded("title text"),
+ endItem = ChipbarEndItem.Error,
+ ),
+ )
+ val chipbarView = getChipbarView()
+
+ underTest.removeView(DEVICE_ID, "test reason")
+
+ verify(windowManager).removeView(chipbarView)
+ }
+
@Test
fun swipeToDismiss_false_neverListensForGesture() {
underTest.displayView(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
deleted file mode 100644
index ffac8f6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.temporarydisplay.chipbar
-
-import android.content.Context
-import android.os.PowerManager
-import android.view.ViewGroup
-import android.view.WindowManager
-import android.view.accessibility.AccessibilityManager
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
-import com.android.systemui.util.view.ViewUtil
-import com.android.systemui.util.wakelock.WakeLock
-
-/** A fake implementation of [ChipbarCoordinator] for testing. */
-class FakeChipbarCoordinator(
- context: Context,
- logger: ChipbarLogger,
- windowManager: WindowManager,
- mainExecutor: DelayableExecutor,
- accessibilityManager: AccessibilityManager,
- configurationController: ConfigurationController,
- dumpManager: DumpManager,
- powerManager: PowerManager,
- falsingManager: FalsingManager,
- falsingCollector: FalsingCollector,
- swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
- viewUtil: ViewUtil,
- vibratorHelper: VibratorHelper,
- wakeLockBuilder: WakeLock.Builder,
- systemClock: SystemClock,
-) :
- ChipbarCoordinator(
- context,
- logger,
- windowManager,
- mainExecutor,
- accessibilityManager,
- configurationController,
- dumpManager,
- powerManager,
- falsingManager,
- falsingCollector,
- swipeChipbarAwayGestureHandler,
- viewUtil,
- vibratorHelper,
- wakeLockBuilder,
- systemClock,
- ) {
- override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
- // Just bypass the animation in tests
- onAnimationEnd.run()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
index a87a950..c539246 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
@@ -22,6 +22,7 @@
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -36,7 +37,7 @@
@Before
fun setUp() {
- underTest = SwipeChipbarAwayGestureHandler(context, mock())
+ underTest = SwipeChipbarAwayGestureHandler(context, FakeDisplayTracker(mContext), mock())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 0a1e3e4..9d518ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -91,6 +91,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.NotificationShadeWindowView;
@@ -287,6 +288,8 @@
private TestableLooper mTestableLooper;
+ private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -333,7 +336,7 @@
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
- mSysUiState = new SysUiState();
+ mSysUiState = new SysUiState(mDisplayTracker);
mSysUiState.addCallback(sysUiFlags -> {
mSysUiStateBubblesManageMenuExpanded =
(sysUiFlags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 7ae47b4..8cae998 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.notetask.NoteTaskInitializer;
+import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -84,6 +85,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
mWMShell = new WMShell(
mContext,
mShellInterface,
@@ -100,6 +102,7 @@
mProtoTracer,
mWakefulnessLifecycle,
mUserTracker,
+ displayTracker,
mNoteTaskInitializer,
mSysUiMainExecutor
);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
index 990db77..f723a9e5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
@@ -23,14 +23,20 @@
isUnlocked: Boolean = true,
isShowingAlternateAuthOnUnlock: Boolean = false,
interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java),
+ isPredictiveBackQsDialogAnim: Boolean = false,
): DialogLaunchAnimator {
return DialogLaunchAnimator(
- FakeCallback(
- isUnlocked = isUnlocked,
- isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
- ),
- interactionJankMonitor,
- fakeLaunchAnimator(),
+ callback =
+ FakeCallback(
+ isUnlocked = isUnlocked,
+ isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
+ ),
+ interactionJankMonitor = interactionJankMonitor,
+ featureFlags =
+ object : AnimationFeatureFlags {
+ override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim
+ },
+ launchAnimator = fakeLaunchAnimator(),
isForTesting = true,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
new file mode 100644
index 0000000..5641832
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyguard.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
+ private val _isLockedOut = MutableStateFlow<Boolean>(false)
+ override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
+
+ fun setLockedOut(lockedOut: Boolean) {
+ _isLockedOut.value = lockedOut
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 15b4736..065fe89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -29,6 +29,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
/** Fake implementation of [KeyguardRepository] */
class FakeKeyguardRepository : KeyguardRepository {
@@ -101,6 +102,13 @@
private val _biometricUnlockSource = MutableStateFlow<BiometricUnlockSource?>(null)
override val biometricUnlockSource: Flow<BiometricUnlockSource?> = _biometricUnlockSource
+ private val _isQuickSettingsVisible = MutableStateFlow(false)
+ override val isQuickSettingsVisible: Flow<Boolean> = _isQuickSettingsVisible.asStateFlow()
+
+ override fun setQuickSettingsVisible(isVisible: Boolean) {
+ _isQuickSettingsVisible.value = isVisible
+ }
+
override fun isKeyguardShowing(): Boolean {
return _isKeyguardShowing.value
}
@@ -169,6 +177,10 @@
_dozeTransitionModel.value = model
}
+ fun setStatusBarState(state: StatusBarState) {
+ _statusBarState.value = state
+ }
+
override fun isUdfpsSupported(): Boolean {
return _isUdfpsSupported.value
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 6c44244..eac1bd1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -22,13 +22,15 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import java.util.UUID
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
/** Fake implementation of [KeyguardTransitionRepository] */
class FakeKeyguardTransitionRepository : KeyguardTransitionRepository {
- private val _transitions = MutableSharedFlow<TransitionStep>()
+ private val _transitions =
+ MutableSharedFlow<TransitionStep>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val transitions: SharedFlow<TransitionStep> = _transitions
suspend fun sendTransitionStep(step: TransitionStep) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeDisplayTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeDisplayTracker.kt
new file mode 100644
index 0000000..6ae7c34
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeDisplayTracker.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings
+
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.view.Display
+import java.util.concurrent.Executor
+
+class FakeDisplayTracker internal constructor(val context: Context) : DisplayTracker {
+ val displayManager: DisplayManager = context.getSystemService(DisplayManager::class.java)!!
+ override var defaultDisplayId: Int = Display.DEFAULT_DISPLAY
+ override var allDisplays: Array<Display> = displayManager.displays
+
+ private val displayCallbacks: MutableList<DisplayTracker.Callback> = ArrayList()
+ private val brightnessCallbacks: MutableList<DisplayTracker.Callback> = ArrayList()
+ override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) {
+ displayCallbacks.add(callback)
+ }
+ override fun addBrightnessChangeCallback(
+ callback: DisplayTracker.Callback,
+ executor: Executor
+ ) {
+ brightnessCallbacks.add(callback)
+ }
+
+ override fun removeCallback(callback: DisplayTracker.Callback) {
+ displayCallbacks.remove(callback)
+ brightnessCallbacks.remove(callback)
+ }
+
+ fun setDefaultDisplay(displayId: Int) {
+ defaultDisplayId = displayId
+ }
+
+ fun setDisplays(displays: Array<Display>) {
+ allDisplays = displays
+ }
+
+ fun triggerOnDisplayAdded(displayId: Int) {
+ notifyCallbacks({ onDisplayAdded(displayId) }, displayCallbacks)
+ }
+
+ fun triggerOnDisplayRemoved(displayId: Int) {
+ notifyCallbacks({ onDisplayRemoved(displayId) }, displayCallbacks)
+ }
+
+ fun triggerOnDisplayChanged(displayId: Int) {
+ notifyCallbacks({ onDisplayChanged(displayId) }, displayCallbacks)
+ }
+
+ fun triggerOnDisplayBrightnessChanged(displayId: Int) {
+ notifyCallbacks({ onDisplayChanged(displayId) }, brightnessCallbacks)
+ }
+
+ private inline fun notifyCallbacks(
+ crossinline action: DisplayTracker.Callback.() -> Unit,
+ list: List<DisplayTracker.Callback>
+ ) {
+ list.forEach { it.action() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
index 4a881a7..fd1b8e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
@@ -41,7 +41,7 @@
}
override fun getStringSet(key: String, defValues: MutableSet<String>?): MutableSet<String>? {
- return data.getOrDefault(key, defValues) as? MutableSet<String>?
+ return (data.getOrDefault(key, defValues) as? Set<String>?)?.toMutableSet()
}
override fun getInt(key: String, defValue: Int): Int {
diff --git a/packages/VpnDialogs/res/values-ky/strings.xml b/packages/VpnDialogs/res/values-ky/strings.xml
index 2087b62..41204aa 100644
--- a/packages/VpnDialogs/res/values-ky/strings.xml
+++ b/packages/VpnDialogs/res/values-ky/strings.xml
@@ -29,7 +29,7 @@
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> тармагына ар дайым туташып турсун деп жөндөлгөн, бирок учурда телефонуңуз ага туташа албай жатат. <xliff:g id="VPN_APP_1">%1$s</xliff:g> тармагына кайра туташканга чейин телефонуңуз жалпыга ачык тармакты пайдаланып турат."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> тармагына ар дайым туташып турсун деп жөндөлгөн, бирок учурда телефонуңуз ага туташа албай жатат. VPN тармагына кайра туташмайынча, Интернет жок болот."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN жөндөөлөрүн өзгөртүү"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN параметрлерин өзгөртүү"</string>
<string name="configure" msgid="4905518375574791375">"Конфигурациялоо"</string>
<string name="disconnect" msgid="971412338304200056">"Ажыратуу"</string>
<string name="open_app" msgid="3717639178595958667">"Колдонмону ачуу"</string>
diff --git a/proto/src/OWNERS b/proto/src/OWNERS
index ccff624..4d898b41 100644
--- a/proto/src/OWNERS
+++ b/proto/src/OWNERS
@@ -3,3 +3,4 @@
per-file camera.proto = file:/services/core/java/com/android/server/camera/OWNERS
per-file system_messages.proto = file:/core/res/OWNERS
per-file altitude.proto = file:/location/OWNERS
+per-file am_capabilities.proto = rpaquay@google.com, sanglardf@google.com
diff --git a/proto/src/am_capabilities.proto b/proto/src/am_capabilities.proto
new file mode 100644
index 0000000..d97bf81
--- /dev/null
+++ b/proto/src/am_capabilities.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package com.android.server.am;
+option java_multiple_files = true;
+
+message Capability {
+ string name = 1;
+}
+
+message Capabilities {
+ repeated Capability values = 1;
+}
diff --git a/rs/jni/Android.bp b/rs/jni/Android.bp
index 9a6fa8e..8a6897c 100644
--- a/rs/jni/Android.bp
+++ b/rs/jni/Android.bp
@@ -51,4 +51,10 @@
"-Wunreachable-code",
"-Wno-deprecated-declarations",
],
+
+ target: {
+ android_riscv64: {
+ enabled: false,
+ },
+ },
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index f4c6cc3..0926f8a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -314,6 +314,9 @@
void unbindImeLocked(AbstractAccessibilityServiceConnection connection);
void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc);
+
+ void setCurrentUserFocusAppearance(int strokeWidth, int color);
+
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b28ab7a..e824205 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -28,6 +28,7 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
import static android.provider.Settings.Secure.CONTRAST_LEVEL;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_MENU_IN_SYSTEM;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static android.view.accessibility.AccessibilityManager.CONTRAST_DEFAULT_VALUE;
import static android.view.accessibility.AccessibilityManager.CONTRAST_NOT_SET;
@@ -170,6 +171,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -215,6 +217,9 @@
private static final String SET_PIP_ACTION_REPLACEMENT =
"setPictureInPictureActionReplacingConnection";
+ @VisibleForTesting
+ static final String MENU_SERVICE_RELATIVE_CLASS_NAME = ".AccessibilityMenuService";
+
private static final char COMPONENT_NAME_SEPARATOR = ':';
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
@@ -253,7 +258,7 @@
private final MagnificationController mMagnificationController;
private final MagnificationProcessor mMagnificationProcessor;
- private final MainHandler mMainHandler;
+ private final Handler mMainHandler;
// Lazily initialized - access through getSystemActionPerformer()
private SystemActionPerformer mSystemActionPerformer;
@@ -407,6 +412,7 @@
@VisibleForTesting
AccessibilityManagerService(
Context context,
+ Handler handler,
PackageManager packageManager,
AccessibilitySecurityPolicy securityPolicy,
SystemActionPerformer systemActionPerformer,
@@ -420,7 +426,7 @@
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mTraceManager = AccessibilityTraceManager.getInstance(
mWindowManagerService.getAccessibilityController(), this, mLock);
- mMainHandler = new MainHandler(mContext.getMainLooper());
+ mMainHandler = handler;
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = packageManager;
mSecurityPolicy = securityPolicy;
@@ -823,6 +829,95 @@
Context.RECEIVER_EXPORTED);
}
+ /**
+ * Migrates the Accessibility Menu to the version provided by the system build,
+ * if necessary based on presence of the service on the device.
+ */
+ @VisibleForTesting
+ void migrateAccessibilityMenuIfNecessaryLocked(AccessibilityUserState userState) {
+ final Set<ComponentName> menuComponentNames = findA11yMenuComponentNamesLocked();
+ final ComponentName menuOutsideSystem = getA11yMenuOutsideSystem(menuComponentNames);
+ final boolean shouldMigrateToMenuInSystem = menuComponentNames.size() == 2
+ && menuComponentNames.contains(ACCESSIBILITY_MENU_IN_SYSTEM)
+ && menuOutsideSystem != null;
+
+ if (!shouldMigrateToMenuInSystem) {
+ if (menuComponentNames.size() == 1) {
+ // If only one Menu package exists then reset its component to the default state.
+ mPackageManager.setComponentEnabledSetting(
+ menuComponentNames.stream().findFirst().get(),
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ PackageManager.DONT_KILL_APP);
+ }
+ return;
+ }
+
+ // Hide Menu-outside-system so that it does not appear in Settings.
+ mPackageManager.setComponentEnabledSetting(
+ menuOutsideSystem,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ // Migrate the accessibility shortcuts.
+ migrateA11yMenuInSettingLocked(userState,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, menuOutsideSystem);
+ migrateA11yMenuInSettingLocked(userState,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, menuOutsideSystem);
+ migrateA11yMenuInSettingLocked(userState,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, menuOutsideSystem);
+ // If Menu-outside-system is currently enabled by the user then automatically
+ // disable it and enable Menu-in-system.
+ migrateA11yMenuInSettingLocked(userState,
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, menuOutsideSystem);
+ }
+
+ /**
+ * Returns all {@link ComponentName}s whose class name ends in {@link
+ * #MENU_SERVICE_RELATIVE_CLASS_NAME}.
+ **/
+ private Set<ComponentName> findA11yMenuComponentNamesLocked() {
+ Set<ComponentName> result = new ArraySet<>();
+ final var flags =
+ PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
+ new Intent(AccessibilityService.SERVICE_INTERFACE), flags, mCurrentUserId)) {
+ final ComponentName componentName = resolveInfo.serviceInfo.getComponentName();
+ if (componentName.getClassName().endsWith(MENU_SERVICE_RELATIVE_CLASS_NAME)) {
+ result.add(componentName);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the first {@link ComponentName} in the provided set that is not equal to {@link
+ * AccessibilityManager#ACCESSIBILITY_MENU_IN_SYSTEM}.
+ */
+ private static ComponentName getA11yMenuOutsideSystem(Set<ComponentName> menuComponentNames) {
+ Optional<ComponentName> menuOutsideSystem = menuComponentNames.stream().filter(
+ name -> !name.equals(ACCESSIBILITY_MENU_IN_SYSTEM)).findFirst();
+ if (menuOutsideSystem.isEmpty()) {
+ return null;
+ }
+ return menuOutsideSystem.get();
+ }
+
+ /**
+ * Replaces <code>toRemove</code> with {@link AccessibilityManager#ACCESSIBILITY_MENU_IN_SYSTEM}
+ * in the requested setting, if present already.
+ */
+ private void migrateA11yMenuInSettingLocked(AccessibilityUserState userState, String setting,
+ ComponentName toRemove) {
+ mTempComponentNameSet.clear();
+ readComponentNamesFromSettingLocked(setting, userState.mUserId, mTempComponentNameSet);
+ if (mTempComponentNameSet.contains(toRemove)) {
+ mTempComponentNameSet.remove(toRemove);
+ mTempComponentNameSet.add(ACCESSIBILITY_MENU_IN_SYSTEM);
+ persistComponentNamesToSettingLocked(setting, mTempComponentNameSet, userState.mUserId);
+ }
+ }
+
// Called only during settings restore; currently supports only the owner user
// TODO: b/22388012
private void restoreLegacyDisplayMagnificationNavBarIfNeededLocked(String newSetting,
@@ -1592,6 +1687,8 @@
mCurrentUserId = userId;
AccessibilityUserState userState = getCurrentUserStateLocked();
+ migrateAccessibilityMenuIfNecessaryLocked(userState);
+
readConfigurationForUserStateLocked(userState);
mSecurityPolicy.onSwitchUserLocked(mCurrentUserId, userState.mEnabledServices);
// Even if reading did not yield change, we have to update
@@ -4027,7 +4124,7 @@
private final ArrayList<Display> mDisplaysList = new ArrayList<>();
private int mSystemUiUid = 0;
- AccessibilityDisplayListener(Context context, MainHandler handler) {
+ AccessibilityDisplayListener(Context context, Handler handler) {
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, handler);
initializeDisplayList();
@@ -4876,4 +4973,11 @@
transaction.apply();
transaction.close();
}
+
+ @Override
+ public void setCurrentUserFocusAppearance(int strokeWidth, int color) {
+ synchronized (mLock) {
+ getCurrentUserStateLocked().setFocusAppearanceLocked(strokeWidth, color);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index d53a080..df913aa 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -43,6 +43,7 @@
import androidx.annotation.Nullable;
+import com.android.internal.R;
import com.android.server.wm.WindowManagerInternal;
import java.util.Arrays;
@@ -63,6 +64,11 @@
private int mDisplayId;
private List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
+ /** The stroke width of the focus rectangle in pixels */
+ private int mFocusStrokeWidth;
+ /** The color of the focus rectangle */
+ private int mFocusColor;
+
ProxyAccessibilityServiceConnection(
Context context,
ComponentName componentName,
@@ -77,6 +83,10 @@
/* systemActionPerformer= */ null, awm, /* activityTaskManagerService= */ null);
mDisplayId = displayId;
setDisplayTypes(DISPLAY_TYPE_PROXY);
+ mFocusStrokeWidth = mContext.getResources().getDimensionPixelSize(
+ R.dimen.accessibility_focus_highlight_stroke_width);
+ mFocusColor = mContext.getResources().getColor(
+ R.color.accessibility_focus_highlight_color);
}
/**
@@ -203,6 +213,48 @@
}
@Override
+ public void setFocusAppearance(int strokeWidth, int color) {
+ synchronized (mLock) {
+ if (!hasRightsToCurrentUserLocked()) {
+ return;
+ }
+
+ if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
+ return;
+ }
+
+ if (getFocusStrokeWidthLocked() == strokeWidth && getFocusColorLocked() == color) {
+ return;
+ }
+
+ mFocusStrokeWidth = strokeWidth;
+ mFocusColor = color;
+ // Sets the appearance data in the A11yUserState for now, since the A11yManagers are not
+ // separated.
+ // TODO(254545943): Separate proxy and non-proxy states so the focus appearance on the
+ // phone is not affected by the appearance of a proxy-ed app.
+ mSystemSupport.setCurrentUserFocusAppearance(mFocusStrokeWidth, mFocusColor);
+ mSystemSupport.onClientChangeLocked(false);
+ }
+ }
+
+ /**
+ * Gets the stroke width of the focus rectangle.
+ * @return The stroke width.
+ */
+ public int getFocusStrokeWidthLocked() {
+ return mFocusStrokeWidth;
+ }
+
+ /**
+ * Gets the color of the focus rectangle.
+ * @return The color.
+ */
+ public int getFocusColorLocked() {
+ return mFocusColor;
+ }
+
+ @Override
public void binderDied() {
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index fed0932..54cdb04 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -178,9 +178,15 @@
if (a11yEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
}
+ for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
+ final ProxyAccessibilityServiceConnection proxy =
+ mProxyA11yServiceConnections.valueAt(i);
+ if (proxy.mRequestTouchExplorationMode) {
+ clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+ }
+ }
return clientState;
- // TODO(b/254545943): When A11yManager is separated, include support for other properties
- // like isTouchExplorationEnabled.
+ // TODO(b/254545943): When A11yManager is separated, include support for other properties.
}
/**
diff --git a/services/api/current.txt b/services/api/current.txt
index e66bf4d..a92ccd4 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -46,6 +46,14 @@
}
+package com.android.server.appop {
+
+ public interface AppOpsManagerLocal {
+ method public boolean isUidInForeground(int);
+ }
+
+}
+
package com.android.server.pm {
public interface PackageManagerLocal {
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 7db27ac..b68adab 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -199,8 +199,10 @@
intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true);
PendingIntent p = PendingIntent.getActivityAsUser(this, /* requestCode= */ 0,
- intent, PendingIntent.FLAG_MUTABLE, /* options= */ null,
- UserHandle.CURRENT);
+ intent,
+ PendingIntent.FLAG_MUTABLE
+ | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
+ /* options= */ null, UserHandle.CURRENT);
if (sDebug) {
Slog.d(TAG, "startActivity add save UI restored with intent=" + intent);
}
diff --git a/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java b/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
index 042bcbd..1990fe2 100644
--- a/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
+++ b/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
@@ -52,4 +52,29 @@
/* name= */ "backup_transport_callback_timeout_millis",
/* defaultValue= */ 300000); // 5 minutes
}
+
+ /**
+ * Retrieves the value of the flag "full_backup_write_to_transport_buffer_size_bytes".
+ * The returned value is max size of a chunk of backup data that is sent to the transport.
+ */
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ public static int getFullBackupWriteToTransportBufferSizeBytes() {
+ return DeviceConfig.getInt(
+ NAMESPACE,
+ /* name= */ "full_backup_write_to_transport_buffer_size_bytes",
+ /* defaultValue= */ 8 * 1024); // 8 KB
+ }
+
+ /**
+ * Retrieves the value of the flag "full_backup_utils_route_buffer_size_bytes".
+ * The returned value is max size of a chunk of backup data that routed from write end of
+ * pipe from BackupAgent, to read end of pipe to Full Backup Task (PFTBT).
+ */
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ public static int getFullBackupUtilsRouteBufferSizeBytes() {
+ return DeviceConfig.getInt(
+ NAMESPACE,
+ /* name= */ "full_backup_utils_route_buffer_size_bytes",
+ /* defaultValue= */ 32 * 1024); // 32 KB
+ }
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 78df304..162046a 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -40,6 +40,7 @@
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupAndRestoreFeatureFlags;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
import com.android.server.backup.OperationStorage;
@@ -142,7 +143,6 @@
}
private static final String TAG = "PFTBT";
-
private UserBackupManagerService mUserBackupManagerService;
private final Object mCancelLock = new Object();
@@ -388,7 +388,9 @@
// Set up to send data to the transport
final int N = mPackages.size();
- final byte[] buffer = new byte[8192];
+ final int chunkSizeInBytes =
+ BackupAndRestoreFeatureFlags.getFullBackupWriteToTransportBufferSizeBytes();
+ final byte[] buffer = new byte[chunkSizeInBytes];
for (int i = 0; i < N; i++) {
mBackupRunner = null;
PackageInfo currentPackage = mPackages.get(i);
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 dbe3cd9..1c0cd87 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -21,6 +21,8 @@
import android.os.ParcelFileDescriptor;
import android.util.Slog;
+import com.android.server.backup.BackupAndRestoreFeatureFlags;
+
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
@@ -31,8 +33,9 @@
* Low-level utility methods for full backup.
*/
public class FullBackupUtils {
+
/**
- * Reads data from pipe and writes it to the stream in chunks of up to 32KB.
+ * Reads data from pipe and writes it to the stream.
*
* @param inPipe - pipe to read the data from.
* @param out - stream to write the data to.
@@ -43,8 +46,9 @@
// We do not take close() responsibility for the pipe FD
FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
DataInputStream in = new DataInputStream(raw);
-
- byte[] buffer = new byte[32 * 1024];
+ final int chunkSizeInBytes =
+ BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes();
+ byte[] buffer = new byte[chunkSizeInBytes];
int chunkTotal;
while ((chunkTotal = in.readInt()) > 0) {
while (chunkTotal > 0) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceConfig.java b/services/companion/java/com/android/server/companion/CompanionDeviceConfig.java
new file mode 100644
index 0000000..05f2eea
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceConfig.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.companion;
+
+import android.provider.DeviceConfig;
+
+/**
+ * Feature flags for companion.
+ */
+public class CompanionDeviceConfig {
+
+ private static final String NAMESPACE_COMPANION = "companion";
+
+ /**
+ * Whether system data syncing for telecom-type data is enabled.
+ */
+ public static final String ENABLE_CONTEXT_SYNC_TELECOM = "enable_context_sync_telecom";
+
+ /**
+ * Returns whether the given flag is currently enabled, with a default value of {@code true}.
+ */
+ public static boolean isEnabled(String flag) {
+ return DeviceConfig.getBoolean(NAMESPACE_COMPANION, flag, /* defaultValue= */ true);
+ }
+
+ /**
+ * Returns whether the given flag is currently enabled.
+ */
+ public static boolean isEnabled(String flag, boolean defaultValue) {
+ return DeviceConfig.getBoolean(NAMESPACE_COMPANION, flag, defaultValue);
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
index 56e777f..7c339d2 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
@@ -22,4 +22,6 @@
abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
abstract void requestCrossDeviceSync(int userId);
+
+ abstract void updateStatus(int userId, boolean shouldSyncCallMetadata);
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
index 7ea1e6c..ae4766a 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
@@ -16,60 +16,171 @@
package com.android.server.companion.datatransfer.contextsync;
+import android.annotation.Nullable;
import android.telecom.Call;
import android.telecom.InCallService;
+import android.telecom.TelecomManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.companion.CompanionDeviceConfig;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
-/** In-call service to sync call metadata across a user's devices. */
+/**
+ * In-call service to sync call metadata across a user's devices. Note that mute and silence are
+ * global states and apply to all current calls.
+ */
public class CallMetadataSyncInCallService extends InCallService {
+ private static final long NOT_VALID = -1L;
+
@VisibleForTesting
- final Set<CrossDeviceCall> mCurrentCalls = new HashSet<>();
+ final Map<Call, CrossDeviceCall> mCurrentCalls = new HashMap<>();
+ @VisibleForTesting
+ boolean mShouldSync;
+ final Call.Callback mTelecomCallback = new Call.Callback() {
+ @Override
+ public void onDetailsChanged(Call call, Call.Details details) {
+ mCurrentCalls.get(call).updateCallDetails(details);
+ }
+ };
+ final CallMetadataSyncCallback mCallMetadataSyncCallback = new CallMetadataSyncCallback() {
+ @Override
+ void processCallControlAction(int crossDeviceCallId, int callControlAction) {
+ final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
+ mCurrentCalls.values());
+ switch (callControlAction) {
+ case android.companion.Telecom.Call.ACCEPT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doAccept();
+ }
+ break;
+ case android.companion.Telecom.Call.REJECT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doReject();
+ }
+ break;
+ case android.companion.Telecom.Call.SILENCE:
+ doSilence();
+ break;
+ case android.companion.Telecom.Call.MUTE:
+ doMute();
+ break;
+ case android.companion.Telecom.Call.UNMUTE:
+ doUnmute();
+ break;
+ case android.companion.Telecom.Call.END:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doEnd();
+ }
+ break;
+ case android.companion.Telecom.Call.PUT_ON_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doPutOnHold();
+ }
+ break;
+ case android.companion.Telecom.Call.TAKE_OFF_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doTakeOffHold();
+ }
+ break;
+ default:
+ }
+ }
+
+ @Override
+ void requestCrossDeviceSync(int userId) {
+ }
+
+ @Override
+ void updateStatus(int userId, boolean shouldSyncCallMetadata) {
+ if (userId == getUserId()) {
+ mShouldSync = shouldSyncCallMetadata;
+ if (shouldSyncCallMetadata) {
+ initializeCalls();
+ } else {
+ mCurrentCalls.clear();
+ }
+ }
+ }
+ };
@Override
public void onCreate() {
super.onCreate();
- mCurrentCalls.addAll(getCalls().stream().map(CrossDeviceCall::new).toList());
+ initializeCalls();
+ }
+
+ private void initializeCalls() {
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
+ && mShouldSync) {
+ mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call,
+ call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState()))));
+ }
+ }
+
+ @Nullable
+ @VisibleForTesting
+ CrossDeviceCall getCallForId(long crossDeviceCallId, Collection<CrossDeviceCall> calls) {
+ if (crossDeviceCallId == NOT_VALID) {
+ return null;
+ }
+ for (CrossDeviceCall crossDeviceCall : calls) {
+ if (crossDeviceCall.getId() == crossDeviceCallId) {
+ return crossDeviceCall;
+ }
+ }
+ return null;
}
@Override
public void onCallAdded(Call call) {
- onCallAdded(new CrossDeviceCall(call));
- }
-
- @VisibleForTesting
- void onCallAdded(CrossDeviceCall call) {
- mCurrentCalls.add(call);
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
+ && mShouldSync) {
+ mCurrentCalls.put(call,
+ new CrossDeviceCall(getPackageManager(), call, getCallAudioState()));
+ }
}
@Override
public void onCallRemoved(Call call) {
- mCurrentCalls.removeIf(crossDeviceCall -> crossDeviceCall.getCall().equals(call));
- }
-
- /** Data holder for a telecom call and additional metadata. */
- public static final class CrossDeviceCall {
- private static final AtomicLong sNextId = new AtomicLong(1);
-
- private final Call mCall;
- private final long mId;
-
- public CrossDeviceCall(Call call) {
- mCall = call;
- mId = sNextId.getAndIncrement();
- }
-
- public Call getCall() {
- return mCall;
- }
-
- public long getId() {
- return mId;
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
+ && mShouldSync) {
+ mCurrentCalls.remove(call);
}
}
-}
+
+ @Override
+ public void onMuteStateChanged(boolean isMuted) {
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
+ && mShouldSync) {
+ mCurrentCalls.values().forEach(call -> call.updateMuted(isMuted));
+ }
+ }
+
+ @Override
+ public void onSilenceRinger() {
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
+ && mShouldSync) {
+ mCurrentCalls.values().forEach(call -> call.updateSilencedIfRinging());
+ }
+ }
+
+ private void doMute() {
+ setMuted(/* shouldMute= */ true);
+ }
+
+ private void doUnmute() {
+ setMuted(/* shouldMute= */ false);
+ }
+
+ private void doSilence() {
+ final TelecomManager telecomManager = getSystemService(TelecomManager.class);
+ if (telecomManager != null) {
+ telecomManager.silenceRinger();
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
new file mode 100644
index 0000000..3d8fb7a
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.companion.datatransfer.contextsync;
+
+import android.app.admin.DevicePolicyManager;
+import android.companion.AssociationInfo;
+import android.companion.ContextSyncMessage;
+import android.companion.Telecom;
+import android.companion.Telecom.Call;
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Monitors connections and sending / receiving of synced data.
+ */
+public class CrossDeviceSyncController {
+
+ private static final String TAG = "CrossDeviceSyncController";
+ private static final int BYTE_ARRAY_SIZE = 64;
+
+ private final Context mContext;
+ private final Callback mCdmCallback;
+ private final Map<Integer, List<AssociationInfo>> mUserIdToAssociationInfo = new HashMap<>();
+ private final Map<Integer, Pair<InputStream, OutputStream>> mAssociationIdToStreams =
+ new HashMap<>();
+ private final Set<Integer> mBlocklist = new HashSet<>();
+
+ private CallMetadataSyncCallback mInCallServiceCallMetadataSyncCallback;
+
+ public CrossDeviceSyncController(Context context, Callback callback) {
+ mContext = context;
+ mCdmCallback = callback;
+ }
+
+ /** Registers the call metadata callback. */
+ public void registerCallMetadataSyncCallback(CallMetadataSyncCallback callback) {
+ mInCallServiceCallMetadataSyncCallback = callback;
+ }
+
+ /** Allow specific associated devices to enable / disable syncing. */
+ public void setSyncEnabled(AssociationInfo associationInfo, boolean enabled) {
+ if (enabled) {
+ if (mBlocklist.contains(associationInfo.getId())) {
+ mBlocklist.remove(associationInfo.getId());
+ openChannel(associationInfo);
+ }
+ } else {
+ if (!mBlocklist.contains(associationInfo.getId())) {
+ mBlocklist.add(associationInfo.getId());
+ closeChannel(associationInfo);
+ }
+ }
+ }
+
+ /**
+ * Opens channels to newly associated devices, and closes channels to newly disassociated
+ * devices.
+ *
+ * TODO(b/265466098): this needs to be limited to just connected devices
+ */
+ public void onAssociationsChanged(int userId, List<AssociationInfo> newAssociationInfoList) {
+ final List<AssociationInfo> existingAssociationInfoList = mUserIdToAssociationInfo.get(
+ userId);
+ // Close channels to newly-disconnected devices.
+ for (AssociationInfo existingAssociationInfo : existingAssociationInfoList) {
+ if (!newAssociationInfoList.contains(existingAssociationInfo) && !mBlocklist.contains(
+ existingAssociationInfo.getId())) {
+ closeChannel(existingAssociationInfo);
+ }
+ }
+ // Open channels to newly-connected devices.
+ for (AssociationInfo newAssociationInfo : newAssociationInfoList) {
+ if (!existingAssociationInfoList.contains(newAssociationInfo) && !mBlocklist.contains(
+ newAssociationInfo.getId())) {
+ openChannel(newAssociationInfo);
+ }
+ }
+ mUserIdToAssociationInfo.put(userId, newAssociationInfoList);
+ }
+
+ private boolean isAdminBlocked(int userId) {
+ return mContext.getSystemService(DevicePolicyManager.class)
+ .getBluetoothContactSharingDisabled(UserHandle.of(userId));
+ }
+
+ /** Stop reading, close streams, and close secure channel. */
+ private void closeChannel(AssociationInfo associationInfo) {
+ // TODO(b/265466098): stop reading from secure channel
+ final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get(
+ associationInfo.getId());
+ if (streams != null) {
+ try {
+ if (streams.first != null) {
+ streams.first.close();
+ }
+ if (streams.second != null) {
+ streams.second.close();
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not close streams for association " + associationInfo.getId(),
+ e);
+ }
+ }
+ mCdmCallback.closeSecureChannel(associationInfo.getId());
+ }
+
+ /** Sync initial snapshot and start reading. */
+ private void openChannel(AssociationInfo associationInfo) {
+ final InputStream is = new ByteArrayInputStream(new byte[BYTE_ARRAY_SIZE]);
+ final OutputStream os = new ByteArrayOutputStream(BYTE_ARRAY_SIZE);
+ mAssociationIdToStreams.put(associationInfo.getId(), new Pair<>(is, os));
+ mCdmCallback.createSecureChannel(associationInfo.getId(), is, os);
+ // TODO(b/265466098): only requestSync for this specific association / connection?
+ mInCallServiceCallMetadataSyncCallback.requestCrossDeviceSync(associationInfo.getUserId());
+ // TODO(b/265466098): start reading from secure channel
+ }
+
+ /**
+ * Sync data to associated devices.
+ *
+ * @param userId The user whose data should be synced.
+ * @param calls The full list of current calls for all users.
+ */
+ public void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls) {
+ final boolean isAdminBlocked = isAdminBlocked(userId);
+ for (AssociationInfo associationInfo : mUserIdToAssociationInfo.get(userId)) {
+ final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get(
+ associationInfo.getId());
+ final ProtoOutputStream pos = new ProtoOutputStream(streams.second);
+ final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
+ for (CrossDeviceCall call : calls) {
+ final long callsToken = pos.start(Telecom.CALLS);
+ pos.write(Call.ID, call.getId());
+ final long originToken = pos.start(Call.ORIGIN);
+ pos.write(Call.Origin.CALLER_ID, call.getReadableCallerId(isAdminBlocked));
+ pos.write(Call.Origin.APP_ICON, call.getCallingAppIcon());
+ pos.write(Call.Origin.APP_NAME, call.getCallingAppName());
+ pos.end(originToken);
+ pos.write(Call.STATUS, call.getStatus());
+ for (int control : call.getControls()) {
+ pos.write(Call.CONTROLS_AVAILABLE, control);
+ }
+ pos.end(callsToken);
+ }
+ pos.end(telecomToken);
+ pos.flush();
+ }
+ }
+
+ /**
+ * Callback to be implemented by CompanionDeviceManagerService.
+ */
+ public interface Callback {
+ /**
+ * Create a secure channel to send messages.
+ */
+ void createSecureChannel(int associationId, InputStream input, OutputStream output);
+
+ /**
+ * Close the secure channel created previously.
+ */
+ void closeSecureChannel(int associationId);
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index d5cc58f..df5e37c 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -46,6 +46,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
@@ -79,6 +80,14 @@
@interface PhysType {
}
+ /**
+ * The maximum length of a device name (in bytes in UTF-8 encoding).
+ *
+ * This limitation comes directly from uinput.
+ * See also UINPUT_MAX_NAME_SIZE in linux/uinput.h
+ */
+ private static final int DEVICE_NAME_MAX_LENGTH = 80;
+
final Object mLock;
/* Token -> file descriptor associations. */
@@ -312,6 +321,34 @@
}
}
+ /**
+ * Validates a device name by checking length and whether a device with the same name
+ * already exists. Throws exceptions if the validation fails.
+ * @param deviceName The name of the device to be validated
+ * @throws DeviceCreationException if {@code deviceName} is not valid.
+ */
+ private void validateDeviceName(String deviceName) throws DeviceCreationException {
+ // Comparison is greater or equal because the device name must fit into a const char*
+ // including the \0-terminator. Therefore the actual number of bytes that can be used
+ // for device name is DEVICE_NAME_MAX_LENGTH - 1
+ if (deviceName.getBytes(StandardCharsets.UTF_8).length >= DEVICE_NAME_MAX_LENGTH) {
+ throw new DeviceCreationException(
+ "Input device name exceeds maximum length of " + DEVICE_NAME_MAX_LENGTH
+ + "bytes: " + deviceName);
+ }
+
+ synchronized (mLock) {
+ InputDeviceDescriptor[] values = mInputDeviceDescriptors.values().toArray(
+ new InputDeviceDescriptor[0]);
+ for (InputDeviceDescriptor value : values) {
+ if (value.mName.equals(deviceName)) {
+ throw new DeviceCreationException(
+ "Input device name already in use: " + deviceName);
+ }
+ }
+ }
+ }
+
private static String createPhys(@PhysType String type) {
return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement());
}
@@ -451,10 +488,10 @@
@VisibleForTesting
void addDeviceForTesting(IBinder deviceToken, int fd, int type, int displayId, String phys,
- int inputDeviceId) {
+ String deviceName, int inputDeviceId) {
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, () -> {
- }, type, displayId, phys, inputDeviceId));
+ }, type, displayId, phys, deviceName, inputDeviceId));
}
}
@@ -565,6 +602,9 @@
private final @Type int mType;
private final int mDisplayId;
private final String mPhys;
+ // The name given to this device by the client. Enforced to be unique within
+ // InputController.
+ private final String mName;
// The input device id that was associated to the device by the InputReader on device
// creation.
private final int mInputDeviceId;
@@ -572,12 +612,13 @@
private final long mCreationOrderNumber;
InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type,
- int displayId, String phys, int inputDeviceId) {
+ int displayId, String phys, String name, int inputDeviceId) {
mFd = fd;
mDeathRecipient = deathRecipient;
mType = type;
mDisplayId = displayId;
mPhys = phys;
+ mName = name;
mInputDeviceId = inputDeviceId;
mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
}
@@ -733,6 +774,7 @@
"Virtual device creation should happen on an auxiliary thread (e.g. binder "
+ "thread) and not from the handler's thread.");
}
+ validateDeviceName(deviceName);
final int fd;
final BinderDeathRecipient binderDeathRecipient;
@@ -769,7 +811,7 @@
synchronized (mLock) {
mInputDeviceDescriptors.put(deviceToken,
new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys,
- inputDeviceId));
+ deviceName, inputDeviceId));
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 5985ce4..3100277 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -88,7 +88,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.function.Consumer;
@@ -114,7 +113,7 @@
private final CameraAccessController mCameraAccessController;
private VirtualAudioController mVirtualAudioController;
@VisibleForTesting
- final Set<Integer> mVirtualDisplayIds = new ArraySet<>();
+ final ArraySet<Integer> mVirtualDisplayIds = new ArraySet<>();
private final OnDeviceCloseListener mOnDeviceCloseListener;
private final IBinder mAppToken;
private final VirtualDeviceParams mParams;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 2395814c..f39c32df 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -67,7 +67,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -471,7 +470,7 @@
}
@Override
- public @NonNull Set<Integer> getDeviceIdsForUid(int uid) {
+ public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) {
ArraySet<Integer> result = new ArraySet<>();
synchronized (mVirtualDeviceManagerLock) {
int size = mVirtualDevices.size();
@@ -587,6 +586,20 @@
}
@Override
+ public @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ int size = mVirtualDevices.size();
+ for (int i = 0; i < size; i++) {
+ VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
+ if (device.getDeviceId() == deviceId) {
+ return new ArraySet<>(device.mVirtualDisplayIds);
+ }
+ }
+ }
+ return new ArraySet<>();
+ }
+
+ @Override
public void registerVirtualDisplayListener(
@NonNull VirtualDisplayListener listener) {
synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 97e9d26..bd072f5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -148,15 +148,16 @@
static_libs: [
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
- "android.hardware.boot-V1.0-java",
- "android.hardware.boot-V1.1-java",
- "android.hardware.boot-V1.2-java",
+ "android.hardware.boot-V1.0-java", // HIDL
+ "android.hardware.boot-V1.1-java", // HIDL
+ "android.hardware.boot-V1.2-java", // HIDL
+ "android.hardware.boot-V1-java", // AIDL
"android.hardware.broadcastradio-V2.0-java", // HIDL
"android.hardware.broadcastradio-V1-java", // AIDL
"android.hardware.health-V1.0-java", // HIDL
"android.hardware.health-V2.0-java", // HIDL
"android.hardware.health-V2.1-java", // HIDL
- "android.hardware.health-V1-java", // AIDL
+ "android.hardware.health-V2-java", // AIDL
"android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.1-java",
@@ -182,6 +183,7 @@
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
"ImmutabilityAnnotation",
+ "securebox",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index a35aa7c..70eeb7f 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -329,11 +329,11 @@
* when the user is first unlocked to update the usage stats package mappings data that might
* be stale or have existed from a restore and belongs to packages that are not installed for
* this user anymore.
- * Note: this is only executed for the system user.
*
+ * @param userId The user to update
* @return {@code true} if the updating was successful, {@code false} otherwise
*/
- public abstract boolean updatePackageMappingsData();
+ public abstract boolean updatePackageMappingsData(@UserIdInt int userId);
/**
* Listener interface for usage events.
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index b70cbe3..9c2de65 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.net.Network;
import com.android.internal.os.BinderCallsStats;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
@@ -82,6 +83,15 @@
public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast);
/**
+ * Informs battery stats of a data packet that woke up the CPU.
+ *
+ * @param network The network over which the packet arrived.
+ * @param elapsedMillis The time of the packet's arrival in elapsed timebase.
+ * @param uid The uid that received the packet.
+ */
+ public abstract void noteCpuWakingNetworkPacket(Network network, long elapsedMillis, int uid);
+
+ /**
* Informs battery stats of binder stats for the given work source UID.
*/
public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index fce44f5..66fa746 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -27,10 +27,16 @@
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.compat.annotation.ChangeId;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApexStagedEvent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IBackgroundInstallControlService;
+import android.content.pm.IPackageManagerNative;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.InstallSourceInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
@@ -48,9 +54,13 @@
import android.hardware.biometrics.SensorProperties.ComponentInfo;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -62,6 +72,7 @@
import android.os.ShellCommand;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.util.PackageUtils;
import android.util.Slog;
@@ -72,6 +83,8 @@
import com.android.internal.os.IBinaryTransparencyService;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.pm.ApexManager;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
import libcore.util.HexEncoding;
@@ -93,7 +106,6 @@
*/
public class BinaryTransparencyService extends SystemService {
private static final String TAG = "TransparencyService";
- private static final String EXTRA_SERVICE = "service";
@VisibleForTesting
static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
@@ -112,7 +124,9 @@
static final long RECORD_MEASUREMENTS_COOLDOWN_MS = 24 * 60 * 60 * 1000;
@VisibleForTesting
- static final String BUNDLE_PACKAGE_INFO = "package-info";
+ static final String BUNDLE_PACKAGE_NAME = "package-name";
+ @VisibleForTesting
+ static final String BUNDLE_PACKAGE_IS_APEX = "package-is-apex";
@VisibleForTesting
static final String BUNDLE_CONTENT_DIGEST_ALGORITHM = "content-digest-algo";
@VisibleForTesting
@@ -131,6 +145,10 @@
// used for indicating newly installed MBAs that are updated (but unused currently)
static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4;
+ @VisibleForTesting
+ static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION =
+ "enable_biometric_property_verification";
+
private static final boolean DEBUG = false; // toggle this for local debug
private final Context mContext;
@@ -138,6 +156,7 @@
// the system time (in ms) the last measurement was taken
private long mMeasurementsLastRecordedMs;
private PackageManagerInternal mPackageManagerInternal;
+ private BiometricLogger mBiometricLogger;
/**
* Guards whether or not measurements of MBA to be performed. When this change is enabled,
@@ -159,7 +178,18 @@
List<Bundle> results = new ArrayList<>();
for (PackageInfo packageInfo : getCurrentInstalledApexs()) {
- Bundle apexMeasurement = measurePackage(packageInfo);
+ PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
+ packageInfo.packageName);
+ if (packageState == null) {
+ Slog.w(TAG, "Package state is unavailable, ignoring the package "
+ + packageInfo.packageName);
+ continue;
+ }
+ Bundle apexMeasurement = measurePackage(packageState);
+ if (apexMeasurement == null) {
+ Slog.w(TAG, "Skipping the missing APEX in " + packageState.getPath());
+ continue;
+ }
results.add(apexMeasurement);
}
@@ -192,26 +222,30 @@
/**
* Perform basic measurement (i.e. content digest) on a given package.
- * @param packageInfo The package to be measured.
+ * @param packageState The package to be measured.
* @return a {@link android.os.Bundle} that packs the measurement result with the following
- * keys: {@link #BUNDLE_PACKAGE_INFO},
+ * keys: {@link #BUNDLE_PACKAGE_NAME},
+ * {@link #BUNDLE_PACKAGE_IS_APEX}
* {@link #BUNDLE_CONTENT_DIGEST_ALGORITHM}
* {@link #BUNDLE_CONTENT_DIGEST}
*/
- private @NonNull Bundle measurePackage(PackageInfo packageInfo) {
+ private @Nullable Bundle measurePackage(PackageState packageState) {
Bundle result = new Bundle();
// compute content digest
if (DEBUG) {
- Slog.d(TAG, "Computing content digest for " + packageInfo.packageName + " at "
- + packageInfo.applicationInfo.sourceDir);
+ Slog.d(TAG, "Computing content digest for " + packageState.getPackageName() + " at "
+ + packageState.getPath());
}
- Map<Integer, byte[]> contentDigests = computeApkContentDigest(
- packageInfo.applicationInfo.sourceDir);
- result.putParcelable(BUNDLE_PACKAGE_INFO, packageInfo);
+ AndroidPackage pkg = packageState.getAndroidPackage();
+ if (pkg == null) {
+ Slog.w(TAG, "Skipping the missing APK in " + packageState.getPath());
+ return null;
+ }
+ Map<Integer, byte[]> contentDigests = computeApkContentDigest(pkg.getBaseApkPath());
+ result.putString(BUNDLE_PACKAGE_NAME, pkg.getPackageName());
if (contentDigests == null) {
- Slog.d(TAG, "Failed to compute content digest for "
- + packageInfo.applicationInfo.sourceDir);
+ Slog.d(TAG, "Failed to compute content digest for " + pkg.getBaseApkPath());
result.putInt(BUNDLE_CONTENT_DIGEST_ALGORITHM, 0);
result.putByteArray(BUNDLE_CONTENT_DIGEST, null);
return result;
@@ -235,6 +269,7 @@
result.putInt(BUNDLE_CONTENT_DIGEST_ALGORITHM, 0);
result.putByteArray(BUNDLE_CONTENT_DIGEST, null);
}
+ result.putBoolean(BUNDLE_PACKAGE_IS_APEX, packageState.isApex());
return result;
}
@@ -243,56 +278,39 @@
/**
* Measures and records digests for *all* covered binaries/packages.
*
- * This method will be called in a Job scheduled to take measurements periodically.
+ * This method will be called in a Job scheduled to take measurements periodically. If the
+ * last measurement was performaned recently (less than RECORD_MEASUREMENT_COOLDOWN_MS
+ * ago), the measurement and recording will be skipped.
*
* Packages that are covered so far are:
* - all APEXs (introduced in Android T)
* - all mainline modules (introduced in Android T)
* - all preloaded apps and their update(s) (new in Android U)
* - dynamically installed mobile bundled apps (MBAs) (new in Android U)
- *
- * @return a {@code List<Bundle>}. Each Bundle item contains values as
- * defined by the return value of {@link #measurePackage(PackageInfo)}.
*/
- public List getMeasurementsForAllPackages() {
- List<Bundle> results = new ArrayList<>();
- PackageManager pm = mContext.getPackageManager();
- Set<String> packagesMeasured = new HashSet<>();
-
+ public void recordMeasurementsForAllPackages() {
// check if we should record the resulting measurements
long currentTimeMs = System.currentTimeMillis();
- boolean record = false;
- if ((currentTimeMs - mMeasurementsLastRecordedMs) >= RECORD_MEASUREMENTS_COOLDOWN_MS) {
- Slog.d(TAG, "Measurement was last taken at " + mMeasurementsLastRecordedMs
- + " and is now updated to: " + currentTimeMs);
- mMeasurementsLastRecordedMs = currentTimeMs;
- record = true;
+ if ((currentTimeMs - mMeasurementsLastRecordedMs) < RECORD_MEASUREMENTS_COOLDOWN_MS) {
+ Slog.d(TAG, "Skip measurement since the last measurement was only taken at "
+ + mMeasurementsLastRecordedMs + " within the cooldown period");
+ return;
}
+ Slog.d(TAG, "Measurement was last taken at " + mMeasurementsLastRecordedMs
+ + " and is now updated to: " + currentTimeMs);
+ mMeasurementsLastRecordedMs = currentTimeMs;
+
+ Set<String> packagesMeasured = new HashSet<>();
// measure all APEXs first
if (DEBUG) {
Slog.d(TAG, "Measuring APEXs...");
}
- for (PackageInfo packageInfo : getCurrentInstalledApexs()) {
- packagesMeasured.add(packageInfo.packageName);
+ List<IBinaryTransparencyService.ApexInfo> allApexInfo = collectAllApexInfo();
+ for (IBinaryTransparencyService.ApexInfo apexInfo : allApexInfo) {
+ packagesMeasured.add(apexInfo.packageName);
- Bundle apexMeasurement = measurePackage(packageInfo);
- results.add(apexMeasurement);
-
- if (record) {
- // compute digests of signing info
- String[] signerDigestHexStrings = computePackageSignerSha256Digests(
- packageInfo.signingInfo);
-
- // log to statsd
- FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED,
- packageInfo.packageName,
- packageInfo.getLongVersionCode(),
- HexEncoding.encodeToString(apexMeasurement.getByteArray(
- BUNDLE_CONTENT_DIGEST), false),
- apexMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM),
- signerDigestHexStrings);
- }
+ recordApexInfo(apexInfo);
}
if (DEBUG) {
Slog.d(TAG, "Measured " + packagesMeasured.size()
@@ -300,57 +318,11 @@
}
// proceed with all preloaded apps
- for (PackageInfo packageInfo : pm.getInstalledPackages(
- PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY
- | PackageManager.GET_SIGNING_CERTIFICATES))) {
- if (packagesMeasured.contains(packageInfo.packageName)) {
- continue;
- }
- packagesMeasured.add(packageInfo.packageName);
-
- int mba_status = MBA_STATUS_PRELOADED;
- if (packageInfo.signingInfo == null) {
- Slog.d(TAG, "Preload " + packageInfo.packageName + " at "
- + packageInfo.applicationInfo.sourceDir + " has likely been updated.");
- mba_status = MBA_STATUS_UPDATED_PRELOAD;
-
- PackageInfo origPackageInfo = packageInfo;
- try {
- packageInfo = pm.getPackageInfo(packageInfo.packageName,
- PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL
- | PackageManager.GET_SIGNING_CERTIFICATES));
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Failed to obtain an updated PackageInfo of "
- + origPackageInfo.packageName, e);
- packageInfo = origPackageInfo;
- mba_status = MBA_STATUS_ERROR;
- }
- }
-
-
- Bundle packageMeasurement = measurePackage(packageInfo);
- results.add(packageMeasurement);
-
- if (record && (mba_status == MBA_STATUS_UPDATED_PRELOAD)) {
- // compute digests of signing info
- String[] signerDigestHexStrings = computePackageSignerSha256Digests(
- packageInfo.signingInfo);
-
- // now we should have all the bits for the atom
- byte[] cDigest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
- FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
- packageInfo.packageName,
- packageInfo.getLongVersionCode(),
- (cDigest != null) ? HexEncoding.encodeToString(cDigest, false) : null,
- packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM),
- signerDigestHexStrings, // signer_cert_digest
- mba_status, // mba_status
- null, // initiator
- null, // initiator_signer_digest
- null, // installer
- null // originator
- );
- }
+ List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo =
+ collectAllUpdatedPreloadInfo(packagesMeasured);
+ for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
+ packagesMeasured.add(appInfo.packageName);
+ writeAppInfoToLog(appInfo);
}
if (DEBUG) {
Slog.d(TAG, "Measured " + packagesMeasured.size()
@@ -359,61 +331,11 @@
if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
// lastly measure all newly installed MBAs
- for (PackageInfo packageInfo : getNewlyInstalledMbas()) {
- if (packagesMeasured.contains(packageInfo.packageName)) {
- continue;
- }
- packagesMeasured.add(packageInfo.packageName);
-
- Bundle packageMeasurement = measurePackage(packageInfo);
- results.add(packageMeasurement);
-
- if (record) {
- // compute digests of signing info
- String[] signerDigestHexStrings = computePackageSignerSha256Digests(
- packageInfo.signingInfo);
-
- // then extract package's InstallSourceInfo
- if (DEBUG) {
- Slog.d(TAG,
- "Extracting InstallSourceInfo for " + packageInfo.packageName);
- }
- InstallSourceInfo installSourceInfo = getInstallSourceInfo(
- packageInfo.packageName);
- String initiator = null;
- SigningInfo initiatorSignerInfo = null;
- String[] initiatorSignerInfoDigest = null;
- String installer = null;
- String originator = null;
-
- if (installSourceInfo != null) {
- initiator = installSourceInfo.getInitiatingPackageName();
- initiatorSignerInfo =
- installSourceInfo.getInitiatingPackageSigningInfo();
- if (initiatorSignerInfo != null) {
- initiatorSignerInfoDigest = computePackageSignerSha256Digests(
- initiatorSignerInfo);
- }
- installer = installSourceInfo.getInstallingPackageName();
- originator = installSourceInfo.getOriginatingPackageName();
- }
-
- // we should now have all the info needed for the atom
- byte[] cDigest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
- FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
- packageInfo.packageName,
- packageInfo.getLongVersionCode(),
- (cDigest != null) ? HexEncoding.encodeToString(cDigest, false)
- : null,
- packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM),
- signerDigestHexStrings,
- MBA_STATUS_NEW_INSTALL, // mba_status
- initiator,
- initiatorSignerInfoDigest,
- installer,
- originator
- );
- }
+ List<IBinaryTransparencyService.AppInfo> allMbaInfo =
+ collectAllMbaInfo(packagesMeasured);
+ for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
+ packagesMeasured.add(appInfo.packageName);
+ writeAppInfoToLog(appInfo);
}
}
if (DEBUG) {
@@ -421,10 +343,159 @@
Slog.d(TAG, "Measured " + packagesMeasured.size()
+ " packages altogether in " + timeSpentMeasuring + "ms");
}
+ }
+ private List<IBinaryTransparencyService.ApexInfo> collectAllApexInfo() {
+ var results = new ArrayList<IBinaryTransparencyService.ApexInfo>();
+ for (PackageInfo packageInfo : getCurrentInstalledApexs()) {
+ PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
+ packageInfo.packageName);
+ if (packageState == null) {
+ Slog.w(TAG, "Package state is unavailable, ignoring the APEX "
+ + packageInfo.packageName);
+ continue;
+ }
+
+ Bundle apexMeasurement = measurePackage(packageState);
+ if (apexMeasurement == null) {
+ Slog.w(TAG, "Skipping the missing APEX in " + packageState.getPath());
+ continue;
+ }
+
+ var apexInfo = new IBinaryTransparencyService.ApexInfo();
+ apexInfo.packageName = packageState.getPackageName();
+ apexInfo.longVersion = packageState.getVersionCode();
+ apexInfo.digest = apexMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
+ apexInfo.digestAlgorithm =
+ apexMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM);
+ apexInfo.signerDigests =
+ computePackageSignerSha256Digests(packageState.getSigningInfo());
+
+ results.add(apexInfo);
+ }
return results;
}
+ private List<IBinaryTransparencyService.AppInfo> collectAllUpdatedPreloadInfo(
+ Set<String> packagesToSkip) {
+ final var results = new ArrayList<IBinaryTransparencyService.AppInfo>();
+
+ PackageManager pm = mContext.getPackageManager();
+ mPackageManagerInternal.forEachPackageState((packageState) -> {
+ if (!packageState.isUpdatedSystemApp()) {
+ return;
+ }
+ if (packagesToSkip.contains(packageState.getPackageName())) {
+ return;
+ }
+
+ Slog.d(TAG, "Preload " + packageState.getPackageName() + " at "
+ + packageState.getPath() + " has likely been updated.");
+
+ Bundle packageMeasurement = measurePackage(packageState);
+ if (packageMeasurement == null) {
+ Slog.w(TAG, "Skipping the missing APK in " + packageState.getPath());
+ return;
+ }
+
+ var appInfo = new IBinaryTransparencyService.AppInfo();
+ appInfo.packageName = packageState.getPackageName();
+ appInfo.longVersion = packageState.getVersionCode();
+ appInfo.digest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
+ appInfo.digestAlgorithm =
+ packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM);
+ appInfo.signerDigests =
+ computePackageSignerSha256Digests(packageState.getSigningInfo());
+ appInfo.mbaStatus = MBA_STATUS_UPDATED_PRELOAD;
+
+ results.add(appInfo);
+ });
+ return results;
+ }
+
+ private List<IBinaryTransparencyService.AppInfo> collectAllMbaInfo(
+ Set<String> packagesToSkip) {
+ var results = new ArrayList<IBinaryTransparencyService.AppInfo>();
+ for (PackageInfo packageInfo : getNewlyInstalledMbas()) {
+ if (packagesToSkip.contains(packageInfo.packageName)) {
+ continue;
+ }
+ PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
+ packageInfo.packageName);
+ if (packageState == null) {
+ Slog.w(TAG, "Package state is unavailable, ignoring the package "
+ + packageInfo.packageName);
+ continue;
+ }
+
+ Bundle packageMeasurement = measurePackage(packageState);
+ if (packageMeasurement == null) {
+ Slog.w(TAG, "Skipping the missing APK in " + packageState.getPath());
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Extracting InstallSourceInfo for " + packageState.getPackageName());
+ }
+ var appInfo = new IBinaryTransparencyService.AppInfo();
+ appInfo.packageName = packageState.getPackageName();
+ appInfo.longVersion = packageState.getVersionCode();
+ appInfo.digest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
+ appInfo.digestAlgorithm =
+ packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM);
+ appInfo.signerDigests =
+ computePackageSignerSha256Digests(packageState.getSigningInfo());
+ appInfo.mbaStatus = MBA_STATUS_NEW_INSTALL;
+
+ // Install source isn't currently available in PackageState (there's a TODO).
+ // Extract manually with another call.
+ InstallSourceInfo installSourceInfo = getInstallSourceInfo(
+ packageState.getPackageName());
+ if (installSourceInfo != null) {
+ appInfo.initiator = installSourceInfo.getInitiatingPackageName();
+ SigningInfo initiatorSignerInfo =
+ installSourceInfo.getInitiatingPackageSigningInfo();
+ if (initiatorSignerInfo != null) {
+ appInfo.initiatorSignerDigests =
+ computePackageSignerSha256Digests(initiatorSignerInfo);
+ }
+ appInfo.installer = installSourceInfo.getInstallingPackageName();
+ appInfo.originator = installSourceInfo.getOriginatingPackageName();
+ }
+
+ results.add(appInfo);
+ }
+ return results;
+ }
+
+ private void recordApexInfo(IBinaryTransparencyService.ApexInfo apexInfo) {
+ // Must order by the proto's field number.
+ FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED,
+ apexInfo.packageName,
+ apexInfo.longVersion,
+ (apexInfo.digest != null) ? HexEncoding.encodeToString(apexInfo.digest, false)
+ : null,
+ apexInfo.digestAlgorithm,
+ apexInfo.signerDigests);
+ }
+
+ private void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) {
+ // Must order by the proto's field number.
+ FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
+ appInfo.packageName,
+ appInfo.longVersion,
+ (appInfo.digest != null) ? HexEncoding.encodeToString(appInfo.digest, false)
+ : null,
+ appInfo.digestAlgorithm,
+ appInfo.signerDigests,
+ appInfo.mbaStatus,
+ appInfo.initiator,
+ appInfo.initiatorSignerDigests,
+ appInfo.installer,
+ appInfo.originator,
+ appInfo.splitName);
+ }
+
/**
* A wrapper around
* {@link ApkSignatureVerifier#verifySignaturesInternal(ParseInput, String, int, boolean)}.
@@ -1049,13 +1120,56 @@
}
private final BinaryTransparencyServiceImpl mServiceImpl;
+ /**
+ * A wrapper of {@link FrameworkStatsLog} for easier testing
+ */
+ @VisibleForTesting
+ public static class BiometricLogger {
+ private static final String TAG = "BiometricLogger";
+
+ private static final BiometricLogger sInstance = new BiometricLogger();
+
+ private BiometricLogger() {}
+
+ public static BiometricLogger getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * A wrapper of {@link FrameworkStatsLog}
+ *
+ * @param sensorId The sensorId of the biometric to be logged
+ * @param modality The modality of the biometric
+ * @param sensorType The sensor type of the biometric
+ * @param sensorStrength The sensor strength of the biometric
+ * @param componentId The component Id of a component of the biometric
+ * @param hardwareVersion The hardware version of a component of the biometric
+ * @param firmwareVersion The firmware version of a component of the biometric
+ * @param serialNumber The serial number of a component of the biometric
+ * @param softwareVersion The software version of a component of the biometric
+ */
+ public void logStats(int sensorId, int modality, int sensorType, int sensorStrength,
+ String componentId, String hardwareVersion, String firmwareVersion,
+ String serialNumber, String softwareVersion) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED,
+ sensorId, modality, sensorType, sensorStrength, componentId, hardwareVersion,
+ firmwareVersion, serialNumber, softwareVersion);
+ }
+ }
+
public BinaryTransparencyService(Context context) {
+ this(context, BiometricLogger.getInstance());
+ }
+
+ @VisibleForTesting
+ BinaryTransparencyService(Context context, BiometricLogger biometricLogger) {
super(context);
mContext = context;
mServiceImpl = new BinaryTransparencyServiceImpl();
mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
mMeasurementsLastRecordedMs = 0;
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mBiometricLogger = biometricLogger;
}
/**
@@ -1070,28 +1184,6 @@
} catch (Throwable t) {
Slog.e(TAG, "Failed to start BinaryTransparencyService.", t);
}
-
- // register a package observer to detect updates to preloads
- mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() {
- @Override
- public void onPackageChanged(String packageName, int uid) {
- // check if the updated package is a preloaded app.
- PackageManager pm = mContext.getPackageManager();
- try {
- pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(
- PackageManager.MATCH_FACTORY_ONLY));
- } catch (PackageManager.NameNotFoundException e) {
- // this means that this package is not a preloaded app
- return;
- }
-
- Slog.d(TAG, "Preload " + packageName + " was updated. Scheduling measurement...");
- UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
- BinaryTransparencyService.this);
- }
- });
-
- // TODO(b/264428429): Register observer for updates to APEXs.
}
/**
@@ -1123,6 +1215,8 @@
Slog.i(TAG, "Scheduling measurements to be taken.");
UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
BinaryTransparencyService.this);
+
+ registerAllPackageUpdateObservers();
}
}
@@ -1146,14 +1240,11 @@
// where this operation might take longer than expected, and so that we don't block
// system_server's main thread.
Executors.defaultThreadFactory().newThread(() -> {
- // we discard the return value of getMeasurementsForAllPackages() as the
- // results of the measurements will be recorded, and that is what we're aiming
- // for with this job.
IBinder b = ServiceManager.getService(Context.BINARY_TRANSPARENCY_SERVICE);
IBinaryTransparencyService iBtsService =
IBinaryTransparencyService.Stub.asInterface(b);
try {
- iBtsService.getMeasurementsForAllPackages();
+ iBtsService.recordMeasurementsForAllPackages();
} catch (RemoteException e) {
Slog.e(TAG, "Taking binary measurements was interrupted.", e);
return;
@@ -1301,7 +1392,7 @@
// Note: none of the component info is a device identifier since every device of a given
// model and build share the same biometric system info (see b/216195167)
for (ComponentInfo componentInfo : prop.getComponentInfo()) {
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED,
+ mBiometricLogger.logStats(
sensorId,
modality,
sensorType,
@@ -1314,7 +1405,19 @@
}
}
- private void collectBiometricProperties() {
+ @VisibleForTesting
+ void collectBiometricProperties() {
+ // Check the flag to determine whether biometric property verification is enabled. It's
+ // disabled by default.
+ if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BIOMETRICS,
+ KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, false)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Do not collect/verify biometric properties. Feature disabled by "
+ + "DeviceConfig");
+ }
+ return;
+ }
+
PackageManager pm = mContext.getPackageManager();
FingerprintManager fpManager = null;
FaceManager faceManager = null;
@@ -1326,25 +1429,51 @@
}
if (fpManager != null) {
- // Log data for each fingerprint sensor
- for (FingerprintSensorPropertiesInternal propInternal :
- fpManager.getSensorPropertiesInternal()) {
- final FingerprintSensorProperties prop =
- FingerprintSensorProperties.from(propInternal);
- logBiometricProperties(prop,
- FrameworkStatsLog
- .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FINGERPRINT,
- toFingerprintSensorType(prop.getSensorType()));
- }
+ final int fpModality = FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FINGERPRINT;
+ fpManager.addAuthenticatorsRegisteredCallback(
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ if (DEBUG) {
+ Slog.d(TAG, "Retrieve fingerprint sensor properties. "
+ + "sensors.size()=" + sensors.size());
+ }
+ // Log data for each fingerprint sensor
+ for (FingerprintSensorPropertiesInternal propInternal : sensors) {
+ final FingerprintSensorProperties prop =
+ FingerprintSensorProperties.from(propInternal);
+ logBiometricProperties(prop,
+ fpModality,
+ toFingerprintSensorType(prop.getSensorType()));
+ }
+ }
+ });
}
if (faceManager != null) {
- // Log data for each face sensor
- for (FaceSensorProperties prop : faceManager.getSensorProperties()) {
- logBiometricProperties(prop,
- FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FACE,
- toFaceSensorType(prop.getSensorType()));
- }
+ final int faceModality = FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FACE;
+ faceManager.addAuthenticatorsRegisteredCallback(
+ new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ if (DEBUG) {
+ Slog.d(TAG, "Retrieve face sensor properties. sensors.size()="
+ + sensors.size());
+ }
+ // Log data for each face sensor
+ for (FaceSensorPropertiesInternal propInternal : sensors) {
+ final FaceSensorProperties prop =
+ FaceSensorProperties.from(propInternal);
+ logBiometricProperties(prop,
+ faceModality,
+ toFaceSensorType(prop.getSensorType()));
+ }
+ }
+ });
}
}
@@ -1354,6 +1483,126 @@
FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
}
+ /**
+ * Listen for APK updates.
+ *
+ * There are two ways available to us to do this:
+ * 1. Register an observer using
+ * {@link PackageManagerInternal#getPackageList(PackageManagerInternal.PackageListObserver)}.
+ * 2. Register broadcast receivers, listening to either {@code ACTION_PACKAGE_ADDED} or
+ * {@code ACTION_PACKAGE_REPLACED}.
+ *
+ * After experimentation, we found that Option #1 does not catch updates to non-staged APEXs.
+ * Thus, we are implementing Option #2 here. More specifically, listening to
+ * {@link Intent#ACTION_PACKAGE_ADDED} allows us to capture all events we care about.
+ *
+ * We did not use {@link Intent#ACTION_PACKAGE_REPLACED} because it unfortunately does not
+ * detect updates to non-staged APEXs. Thus, we rely on {@link Intent#EXTRA_REPLACING} to
+ * filter out new installation from updates instead.
+ */
+ private void registerApkAndNonStagedApexUpdateListener() {
+ Slog.d(TAG, "Registering APK & Non-Staged APEX updates...");
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package"); // this is somehow necessary
+ mContext.registerReceiver(new PackageUpdatedReceiver(), filter);
+ }
+
+ /**
+ * Listen for staged-APEX updates.
+ *
+ * This method basically covers cases that are not caught by
+ * {@link #registerApkAndNonStagedApexUpdateListener()}, namely updates to APEXs that are staged
+ * for the subsequent reboot.
+ */
+ private void registerStagedApexUpdateObserver() {
+ Slog.d(TAG, "Registering APEX updates...");
+ IPackageManagerNative iPackageManagerNative = IPackageManagerNative.Stub.asInterface(
+ ServiceManager.getService("package_native"));
+ if (iPackageManagerNative == null) {
+ Slog.e(TAG, "IPackageManagerNative is null");
+ return;
+ }
+
+ try {
+ iPackageManagerNative.registerStagedApexObserver(new IStagedApexObserver.Stub() {
+ @Override
+ public void onApexStaged(ApexStagedEvent event) throws RemoteException {
+ Slog.d(TAG, "A new APEX has been staged for update. There are currently "
+ + event.stagedApexModuleNames.length + " APEX(s) staged for update. "
+ + "Scheduling measurement...");
+ UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
+ BinaryTransparencyService.this);
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register a StagedApexObserver.");
+ }
+ }
+
+ private boolean isPackagePreloaded(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(
+ PackageManager.MATCH_FACTORY_ONLY));
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isPackageAnApex(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
+ return packageInfo.isApex;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private class PackageUpdatedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
+ return;
+ }
+
+ Uri data = intent.getData();
+ if (data == null) {
+ Slog.e(TAG, "Shouldn't happen: intent data is null!");
+ return;
+ }
+
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ Slog.d(TAG, "Not an update. Skipping...");
+ return;
+ }
+
+ String packageName = data.getSchemeSpecificPart();
+ // now we've got to check what package is this
+ if (isPackagePreloaded(packageName) || isPackageAnApex(packageName)) {
+ Slog.d(TAG, packageName + " was updated. Scheduling measurement...");
+ UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
+ BinaryTransparencyService.this);
+ }
+ }
+ }
+
+ /**
+ * Register observers for APK and APEX updates. The current implementation breaks this process
+ * into 2 cases/methods because PackageManager does not offer a unified interface to register
+ * for all package updates in a universal and comprehensive manner.
+ * Thus, the observers will be invoked when either
+ * i) APK or non-staged APEX update; or
+ * ii) APEX staging happens.
+ * This will then be used as signals to schedule measurement for the relevant binaries.
+ */
+ private void registerAllPackageUpdateObservers() {
+ registerApkAndNonStagedApexUpdateListener();
+ registerStagedApexUpdateObserver();
+ }
+
private String translateContentDigestAlgorithmIdToString(int algorithmId) {
switch (algorithmId) {
case ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256:
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 4e5ce88..cfd22e8 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2250,14 +2250,19 @@
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
+ boolean preciseCallStateChanged = false;
mRingingCallState[phoneId] = ringingCallState;
mForegroundCallState[phoneId] = foregroundCallState;
mBackgroundCallState[phoneId] = backgroundCallState;
- mPreciseCallState[phoneId] = new PreciseCallState(
+ PreciseCallState preciseCallState = new PreciseCallState(
ringingCallState, foregroundCallState,
backgroundCallState,
DisconnectCause.NOT_VALID,
PreciseDisconnectCause.NOT_VALID);
+ if (!preciseCallState.equals(mPreciseCallState[phoneId])) {
+ preciseCallStateChanged = true;
+ mPreciseCallState[phoneId] = preciseCallState;
+ }
boolean notifyCallState = true;
if (mCallQuality == null) {
log("notifyPreciseCallState: mCallQuality is null, "
@@ -2271,6 +2276,8 @@
mCallNetworkType[phoneId] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
mCallQuality[phoneId] = createCallQuality();
}
+ List<CallState> prevCallStateList = new ArrayList<>();
+ prevCallStateList.addAll(mCallStateLists.get(phoneId));
mCallStateLists.get(phoneId).clear();
if (foregroundCallState != PreciseCallState.PRECISE_CALL_STATE_NOT_VALID
&& foregroundCallState != PreciseCallState.PRECISE_CALL_STATE_IDLE) {
@@ -2330,6 +2337,9 @@
}
mCallStateLists.get(phoneId).add(builder.build());
}
+ if (prevCallStateList.equals(mCallStateLists.get(phoneId))) {
+ notifyCallState = false;
+ }
boolean hasOngoingCall = false;
for (CallState cs : mCallStateLists.get(phoneId)) {
if (cs.getCallState() != PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED) {
@@ -2343,23 +2353,30 @@
}
}
- for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
+ if (preciseCallStateChanged) {
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
- if (notifyCallState && r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
+ }
+
+ if (notifyCallState) {
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
}
@@ -3976,6 +3993,66 @@
}
}
+ /**
+ * Returns a string representation of the radio technology (network type)
+ * currently in use on the device.
+ * @param type for which network type is returned
+ * @return the name of the radio technology
+ *
+ */
+ private String getNetworkTypeName(@Annotation.NetworkType int type) {
+ switch (type) {
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ return "GPRS";
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ return "EDGE";
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ return "UMTS";
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ return "HSDPA";
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ return "HSUPA";
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ return "HSPA";
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ return "CDMA";
+ case TelephonyManager.NETWORK_TYPE_EVDO_0:
+ return "CDMA - EvDo rev. 0";
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ return "CDMA - EvDo rev. A";
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ return "CDMA - EvDo rev. B";
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ return "CDMA - 1xRTT";
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return "LTE";
+ case TelephonyManager.NETWORK_TYPE_EHRPD:
+ return "CDMA - eHRPD";
+ case TelephonyManager.NETWORK_TYPE_IDEN:
+ return "iDEN";
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ return "HSPA+";
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ return "GSM";
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+ return "TD_SCDMA";
+ case TelephonyManager.NETWORK_TYPE_IWLAN:
+ return "IWLAN";
+
+ //TODO: This network type is marked as hidden because it is not a
+ // true network type and we are looking to remove it completely from the available list
+ // of network types. Since this method is only used for logging, in the event that this
+ // network type is selected, the log will read as "Unknown."
+ //case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ // return "LTE_CA";
+
+ case TelephonyManager.NETWORK_TYPE_NR:
+ return "NR";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
/** Returns a new PreciseCallState object with default values. */
private static PreciseCallState createPreciseCallState() {
return new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_NOT_VALID,
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 73bb8d7..f0b168d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1272,7 +1272,8 @@
: (wasStartRequested || !r.getConnections().isEmpty()
? SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_HOT
: SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM),
- getShortProcessNameForStats(callingUid, callingProcessName));
+ getShortProcessNameForStats(callingUid, callingProcessName),
+ getShortServiceNameForStats(r));
if (r.startRequested && addToStarting) {
boolean first = smap.mStartingBackground.size() == 0;
@@ -1311,6 +1312,11 @@
return processName;
}
+ private @Nullable String getShortServiceNameForStats(@NonNull ServiceRecord r) {
+ final ComponentName cn = r.getComponentName();
+ return cn != null ? cn.getShortClassName() : null;
+ }
+
private void stopServiceLocked(ServiceRecord service, boolean enqueueOomAdj) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "stopServiceLocked()");
@@ -3488,7 +3494,8 @@
: (wasStartRequested || hadConnections
? SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_HOT
: SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM),
- getShortProcessNameForStats(callingUid, callerApp.processName));
+ getShortProcessNameForStats(callingUid, callerApp.processName),
+ getShortServiceNameForStats(s));
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
+ ": received=" + b.intent.received
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 70f07a9..ef79351 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -783,7 +783,7 @@
"no_kill_cached_processes_post_boot_completed_duration_millis";
/** @see mEnforceReceiverExportedFlagRequirement */
- private static final boolean DEFAULT_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT = false;
+ private static final boolean DEFAULT_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT = true;
/** @see #mNoKillCachedProcessesUntilBootCompleted */
private static final boolean DEFAULT_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED = true;
@@ -951,7 +951,7 @@
"short_fgs_timeout_duration";
/** @see #KEY_SHORT_FGS_TIMEOUT_DURATION */
- static final long DEFAULT_SHORT_FGS_TIMEOUT_DURATION = 60_000;
+ static final long DEFAULT_SHORT_FGS_TIMEOUT_DURATION = 3 * 60_000;
/** @see #KEY_SHORT_FGS_TIMEOUT_DURATION */
public volatile long mShortFgsTimeoutDuration = DEFAULT_SHORT_FGS_TIMEOUT_DURATION;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index de87a0c..10d50c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8432,15 +8432,13 @@
t.traceEnd();
}
+ boolean isBootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
+
// Some systems - like automotive - will explicitly unlock system user then switch
- // to a secondary user. Hence, we don't want to send duplicate broadcasts for
- // the system user here.
+ // to a secondary user.
// TODO(b/242195409): this workaround shouldn't be necessary once we move
// the headless-user start logic to UserManager-land.
- final boolean isBootingSystemUser = (currentUserId == UserHandle.USER_SYSTEM)
- && !UserManager.isHeadlessSystemUserMode();
-
- if (isBootingSystemUser) {
+ if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
t.traceBegin("startHomeOnAllDisplays");
mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
t.traceEnd();
@@ -8452,6 +8450,10 @@
if (isBootingSystemUser) {
+ // Need to send the broadcasts for the system user here because
+ // UserController#startUserInternal will not send them for the system user starting,
+ // It checks if the user state already exists, which is always the case for the
+ // system user.
t.traceBegin("sendUserStartBroadcast");
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -14254,6 +14256,16 @@
}
}
+ if (Process.isSdkSandboxUid(realCallingUid)) {
+ SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
+ SdkSandboxManagerLocal.class);
+ if (sdkSandboxManagerLocal == null) {
+ throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
+ + " a broadcast from an SDK sandbox uid.");
+ }
+ sdkSandboxManagerLocal.enforceAllowedToSendBroadcast(intent);
+ }
+
boolean timeoutExempt = false;
if (action != null) {
@@ -14264,16 +14276,6 @@
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
- if (Process.isSdkSandboxUid(realCallingUid)) {
- SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
- SdkSandboxManagerLocal.class);
- if (sdkSandboxManagerLocal == null) {
- throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
- + " a broadcast from an SDK sandbox uid.");
- }
- sdkSandboxManagerLocal.enforceAllowedToSendBroadcast(intent);
- }
-
switch (action) {
case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
UserManagerInternal umInternal = LocalServices.getService(
@@ -17842,11 +17844,9 @@
*/
synchronized (wmLock) {
if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
- setDebugApp(aInfo.processName, true, false);
- }
-
- if ((startFlags & ActivityManager.START_FLAG_DEBUG_SUSPEND) != 0) {
- setDebugApp(aInfo.processName, true, false, true);
+ boolean suspend =
+ (startFlags & ActivityManager.START_FLAG_DEBUG_SUSPEND) != 0;
+ setDebugApp(aInfo.processName, true, false, suspend);
}
if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
@@ -18498,6 +18498,12 @@
}
@Override
+ public boolean isProcessFrozen(int pid) {
+ enforceCallingPermission(permission.DUMP, "isProcessFrozen()");
+ return mOomAdjuster.mCachedAppOptimizer.isProcessFrozen(pid);
+ }
+
+ @Override
@ReasonCode
public int getBackgroundRestrictionExemptionReason(int uid) {
enforceCallingPermission(android.Manifest.permission.DEVICE_POWER,
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0a73eaa..1e97285 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -114,6 +114,8 @@
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.util.MemInfoReader;
import com.android.server.am.LowMemDetector.MemFactor;
+import com.android.server.am.nano.Capabilities;
+import com.android.server.am.nano.Capability;
import com.android.server.compat.PlatformCompat;
import com.android.server.utils.Slogf;
@@ -197,6 +199,9 @@
final boolean mDumping;
+ private static final String[] CAPABILITIES = {"start.suspend"};
+
+
ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) {
mInterface = service;
mTaskInterface = service.mActivityTaskManager;
@@ -378,6 +383,8 @@
return runListDisplaysForStartingUsers(pw);
case "set-foreground-service-delegate":
return runSetForegroundServiceDelegate(pw);
+ case "capabilities":
+ return runCapabilities(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -387,6 +394,46 @@
return -1;
}
+ int runCapabilities(PrintWriter pw) throws RemoteException {
+ final PrintWriter err = getErrPrintWriter();
+ boolean outputAsProtobuf = false;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--protobuf")) {
+ outputAsProtobuf = true;
+ } else {
+ err.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+
+ if (outputAsProtobuf) {
+ Capabilities capabilities = new Capabilities();
+ capabilities.values = new Capability[CAPABILITIES.length];
+ for (int i = 0; i < CAPABILITIES.length; i++) {
+ Capability cap = new Capability();
+ cap.name = CAPABILITIES[i];
+ capabilities.values[i] = cap;
+ }
+
+ try {
+ getRawOutputStream().write(Capabilities.toByteArray(capabilities));
+ } catch (IOException e) {
+ pw.println("Error while serializing capabilities protobuffer");
+ }
+
+ } else {
+ // Unfortunately we don't have protobuf text format capabilities here.
+ // Fallback to line separated list instead for text parser.
+ pw.println("Format: 1");
+ for (String capability : CAPABILITIES) {
+ pw.println(capability);
+ }
+ }
+ return 0;
+ }
+
private Intent makeIntent(int defUser) throws URISyntaxException {
mStartFlags = 0;
mWaitOption = false;
@@ -3830,9 +3877,11 @@
pw.println(" Print this help text.");
pw.println(" start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]");
pw.println(" [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]");
- pw.println(" [--track-allocation] [--user <USER_ID> | current] <INTENT>");
+ pw.println(" [--track-allocation] [--user <USER_ID> | current] [--suspend]");
+ pw.println(" <INTENT>");
pw.println(" Start an Activity. Options are:");
pw.println(" -D: enable debugging");
+ pw.println(" --suspend: debugged app suspend threads at startup (only with -D)");
pw.println(" -N: enable native debugging");
pw.println(" -W: wait for launch to complete");
pw.println(" --start-profiler <FILE>: start profiler and send results to <FILE>");
@@ -4133,6 +4182,9 @@
pw.println(" Start ignoring delivery group policy set for a broadcast action");
pw.println(" clear-ignore-delivery-group-policy <ACTION>");
pw.println(" Stop ignoring delivery group policy set for a broadcast action");
+ pw.println(" capabilities [--protobuf]");
+ pw.println(" Output am supported features (text format). Options are:");
+ pw.println(" --protobuf: format output using protobuffer");
Intent.printIntentArgsHelp(pw, "");
}
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index e06ce2c9..4ad43fa 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1831,7 +1831,7 @@
final BatteryStatsImpl bstats = mService.mBatteryStatsService.getActiveStatistics();
synchronized (bstats) {
if (haveNewCpuStats) {
- if (bstats.startAddingCpuLocked()) {
+ if (bstats.startAddingCpuStatsLocked()) {
int totalUTime = 0;
int totalSTime = 0;
final int statsCount = mProcessCpuTracker.countStats();
@@ -1877,9 +1877,10 @@
final int irqTime = mProcessCpuTracker.getLastIrqTime();
final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
final int idleTime = mProcessCpuTracker.getLastIdleTime();
- bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,
+ bstats.addCpuStatsLocked(totalUTime, totalSTime, userTime,
systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
}
+ bstats.finishAddingCpuStatsLocked();
}
if (mLastWriteTime < (now - BATTERY_STATS_TIME)) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0327d16..f09622f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -457,6 +457,11 @@
}
@Override
+ public void noteCpuWakingNetworkPacket(Network network, long elapsedMillis, int uid) {
+ Slog.d(TAG, "Wakeup due to incoming packet on network " + network + " to uid " + uid);
+ }
+
+ @Override
public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
Collection<BinderCallsStats.CallStat> callStats) {
synchronized (BatteryStatsService.this.mLock) {
@@ -2900,11 +2905,9 @@
if (DBG) Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
awaitCompletion();
- synchronized (mStats) {
- mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
- if (writeData) {
- mStats.writeAsyncLocked();
- }
+ mStats.dump(mContext, pw, flags, reqUid, historyStart);
+ if (writeData) {
+ mStats.writeAsyncLocked();
}
pw.println();
mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "), SystemClock.elapsedRealtime());
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index d7cca60..d29c327 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -265,15 +265,9 @@
*/
private boolean replaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
@NonNull BroadcastConsumer replacedBroadcastConsumer, boolean wouldBeSkipped) {
- final int count = mPendingQueues.size();
- for (int i = 0; i < count; ++i) {
- final ArrayDeque<SomeArgs> queue = mPendingQueues.get(i);
- if (replaceBroadcastInQueue(queue, record, recordIndex,
- replacedBroadcastConsumer, wouldBeSkipped)) {
- return true;
- }
- }
- return false;
+ final ArrayDeque<SomeArgs> queue = getQueueForBroadcast(record);
+ return replaceBroadcastInQueue(queue, record, recordIndex,
+ replacedBroadcastConsumer, wouldBeSkipped);
}
/**
@@ -1113,6 +1107,7 @@
pw.print(" because ");
pw.print(reasonToString(mRunnableAtReason));
pw.println();
+ pw.print("mProcessCached="); pw.println(mProcessCached);
pw.increaseIndent();
if (mActive != null) {
dumpRecord("ACTIVE", now, pw, mActive, mActiveIndex);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 410bc41..2689193 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1509,6 +1509,12 @@
return profile;
}
+ boolean isProcessFrozen(int pid) {
+ synchronized (mProcLock) {
+ return mFrozenProcesses.contains(pid);
+ }
+ }
+
@VisibleForTesting
static final class SingleCompactionStats {
private static final float STATSD_SAMPLE_RATE = 0.1f;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 491abc4..3357708 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -198,7 +198,7 @@
//todo(b/236121588) MediaProvider should not be installed in clone profile.
final UserProperties userProps = umInternal.getUserProperties(userId);
final boolean isMediaSharedWithParent =
- userProps != null && userProps.getIsMediaSharedWithParent();
+ userProps != null && userProps.isMediaSharedWithParent();
if (!isAuthorityRedirectedForCloneProfile(name) || !isMediaSharedWithParent) {
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
@@ -1121,11 +1121,15 @@
final String permissionCheck =
checkContentProviderPermission(cpi, callingPid, callingUid,
userId, checkUser, null);
- if (permissionCheck != null) {
+ final boolean grantCheck = mService.checkUriPermission(uri, callingPid, callingUid,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION , userId, null)
+ == PackageManager.PERMISSION_GRANTED;
+
+ if (!grantCheck && permissionCheck != null) {
FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_FRAMEWORK_PERMISSION,
callingUid, authority, type);
- } else if (cpi.forceUriPermissions
+ } else if (!grantCheck && cpi.forceUriPermissions
&& holder.provider.checkUriPermission(attributionSource,
uri, callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
!= PermissionChecker.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 81655cf..114e2c1 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -143,6 +143,7 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* All of the code required to compute proc states and oom_adj values.
@@ -743,6 +744,45 @@
queue.offer(provider);
provider.mState.setReachable(true);
}
+ // See if this process has any corresponding SDK sandbox processes running, and if so
+ // scan them as well.
+ final List<ProcessRecord> sdkSandboxes =
+ mProcessList.getSdkSandboxProcessesForAppLocked(pr.uid);
+ final int numSdkSandboxes = sdkSandboxes != null ? sdkSandboxes.size() : 0;
+ for (int i = numSdkSandboxes - 1; i >= 0; i--) {
+ ProcessRecord sdkSandbox = sdkSandboxes.get(i);
+ containsCycle |= sdkSandbox.mState.isReachable();
+ if (sdkSandbox.mState.isReachable()) {
+ continue;
+ }
+ queue.offer(sdkSandbox);
+ sdkSandbox.mState.setReachable(true);
+ }
+ // If this process is a sandbox itself, also scan the app on whose behalf its running
+ if (pr.isSdkSandbox) {
+ for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) {
+ ServiceRecord s = psr.getRunningServiceAt(is);
+ ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+ s.getConnections();
+ for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) {
+ ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
+ for (int i = clist.size() - 1; i >= 0; i--) {
+ ConnectionRecord cr = clist.get(i);
+ ProcessRecord attributedApp = cr.binding.attributedClient;
+ if (attributedApp == null || attributedApp == pr
+ || ((attributedApp.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ)
+ && (attributedApp.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) {
+ continue;
+ }
+ if (attributedApp.mState.isReachable()) {
+ continue;
+ }
+ queue.offer(attributedApp);
+ attributedApp.mState.setReachable(true);
+ }
+ }
+ }
+ }
}
int size = processes.size();
@@ -2131,6 +2171,11 @@
boolean trackedProcState = false;
ProcessRecord client = cr.binding.client;
+ if (app.isSdkSandbox && cr.binding.attributedClient != null) {
+ // For SDK sandboxes, use the attributed client (eg the app that
+ // requested the sandbox)
+ client = cr.binding.attributedClient;
+ }
final ProcessStateRecord cstate = client.mState;
if (computeClients) {
computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now,
@@ -2377,12 +2422,12 @@
state.setAdjType(adjType);
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE);
- state.setAdjSource(cr.binding.client);
+ state.setAdjSource(client);
state.setAdjSourceProcState(clientProcState);
state.setAdjTarget(s.instanceName);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
- + ": " + app + ", due to " + cr.binding.client
+ + ": " + app + ", due to " + client
+ " adj=" + adj + " procState="
+ ProcessList.makeProcStateString(procState));
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index d58fd2d..14ecf9f 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -452,16 +452,11 @@
resolvedType = key.requestResolvedType;
}
- // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
- // to ensure that we can launch the pending intent with a consistent launch mode even
- // if the provided PendingIntent is immutable (ie. to force an activity to launch into
- // a new task, or to launch multiple instances if supported by the app)
+ // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
+ // can specify a consistent launch mode even if the PendingIntent is immutable
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
- // TODO(b/254490217): Move this check into SafeActivityOptions
- if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
- finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
- }
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
// Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3a40fc7..04c0d64 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -485,6 +485,12 @@
final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
/**
+ * The currently running SDK sandbox processes for a uid.
+ */
+ @GuardedBy("mService")
+ final SparseArray<ArrayList<ProcessRecord>> mSdkSandboxes = new SparseArray<>();
+
+ /**
* Managees the {@link android.app.ApplicationExitInfo} records.
*/
@GuardedBy("mAppExitInfoTracker")
@@ -2990,6 +2996,14 @@
if (proc.isolated) {
mIsolatedProcesses.put(proc.uid, proc);
}
+ if (proc.isSdkSandbox) {
+ ArrayList<ProcessRecord> sdkSandboxes = mSdkSandboxes.get(proc.uid);
+ if (sdkSandboxes == null) {
+ sdkSandboxes = new ArrayList<>();
+ }
+ sdkSandboxes.add(proc);
+ mSdkSandboxes.put(Process.getAppUidForSdkSandboxUid(proc.uid), sdkSandboxes);
+ }
}
@GuardedBy("mService")
@@ -3030,6 +3044,19 @@
return ret;
}
+ /**
+ * Returns the associated SDK sandbox processes for a UID. Note that this does
+ * NOT return a copy, so callers should not modify the result, or use it outside
+ * of the lock scope.
+ *
+ * @param uid UID to return sansdbox processes for
+ */
+ @Nullable
+ @GuardedBy("mService")
+ List<ProcessRecord> getSdkSandboxProcessesForAppLocked(int uid) {
+ return mSdkSandboxes.get(uid);
+ }
+
@GuardedBy("mService")
ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid, boolean isSdkSandbox, int sdkSandboxUid,
@@ -3135,6 +3162,16 @@
if (record != null && record.appZygote) {
removeProcessFromAppZygoteLocked(record);
}
+ if (record != null && record.isSdkSandbox) {
+ final int appUid = Process.getAppUidForSdkSandboxUid(uid);
+ final ArrayList<ProcessRecord> sdkSandboxesForUid = mSdkSandboxes.get(appUid);
+ if (sdkSandboxesForUid != null) {
+ sdkSandboxesForUid.remove(record);
+ if (sdkSandboxesForUid.size() == 0) {
+ mSdkSandboxes.remove(appUid);
+ }
+ }
+ }
mAppsInBackgroundRestricted.remove(record);
return old;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 31f5e45..8ce9889 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2907,7 +2907,7 @@
return false;
}
final UserProperties properties = getUserProperties(userId);
- if (properties == null || !properties.getIsCredentialSharableWithParent()) {
+ if (properties == null || !properties.isCredentialShareableWithParent()) {
return false;
}
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
index 7d9b272..a9a77bf 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -593,16 +593,19 @@
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
assertCalledByPackageOwner(callingPackage);
- for (ClientRequest cr : mExistingClientRequests) {
- if (cr.getPackageName().equals(callingPackage)) {
- AmbientContextManagerPerUserService service =
- getAmbientContextManagerPerUserServiceForEventTypes(
- UserHandle.getCallingUserId(), cr.getRequest().getEventTypes());
- if (service != null) {
- service.onUnregisterObserver(callingPackage);
- } else {
- Slog.w(TAG, "onUnregisterObserver unavailable user_id: "
- + UserHandle.getCallingUserId());
+ synchronized (mLock) {
+ for (ClientRequest cr : mExistingClientRequests) {
+ if (cr.getPackageName().equals(callingPackage)) {
+ AmbientContextManagerPerUserService service =
+ getAmbientContextManagerPerUserServiceForEventTypes(
+ UserHandle.getCallingUserId(),
+ cr.getRequest().getEventTypes());
+ if (service != null) {
+ service.onUnregisterObserver(callingPackage);
+ } else {
+ Slog.w(TAG, "onUnregisterObserver unavailable user_id: "
+ + UserHandle.getCallingUserId());
+ }
}
}
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index d195103..893c8b5 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -40,6 +40,7 @@
import android.app.GameState;
import android.app.IGameManagerService;
import android.app.IGameModeListener;
+import android.app.IUidObserver;
import android.app.StatsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -165,38 +166,19 @@
private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>();
@Nullable
private final GameServiceController mGameServiceController;
+ private final Object mUidObserverLock = new Object();
+ @VisibleForTesting
+ @Nullable
+ final UidObserver mUidObserver;
+ @GuardedBy("mUidObserverLock")
+ private final Set<Integer> mForegroundGameUids = new HashSet<>();
public GameManagerService(Context context) {
this(context, createServiceThread().getLooper());
}
GameManagerService(Context context, Looper looper) {
- mContext = context;
- mHandler = new SettingsHandler(looper);
- mPackageManager = mContext.getPackageManager();
- mUserManager = mContext.getSystemService(UserManager.class);
- mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
- mSystemDir = new File(Environment.getDataDirectory(), "system");
- mSystemDir.mkdirs();
- FileUtils.setPermissions(mSystemDir.toString(),
- FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
- -1, -1);
- mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir,
- GAME_MODE_INTERVENTION_LIST_FILE_NAME));
- FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR
- | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
- -1, -1);
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
- mGameServiceController = new GameServiceController(
- context, BackgroundThread.getExecutor(),
- new GameServiceProviderSelectorImpl(
- context.getResources(),
- context.getPackageManager()),
- new GameServiceProviderInstanceFactoryImpl(context));
- } else {
- mGameServiceController = null;
- }
+ this(context, looper, Environment.getDataDirectory());
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -227,6 +209,14 @@
} else {
mGameServiceController = null;
}
+ mUidObserver = new UidObserver();
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not register UidObserver");
+ }
}
@Override
@@ -2152,4 +2142,66 @@
* load dynamic library for frame rate overriding JNI calls
*/
private static native void nativeSetOverrideFrameRate(int uid, float frameRate);
+
+ final class UidObserver extends IUidObserver.Stub {
+ @Override
+ public void onUidIdle(int uid, boolean disabled) {}
+
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ synchronized (mUidObserverLock) {
+ disableGameMode(uid);
+ }
+ }
+
+ @Override
+ public void onUidActive(int uid) {}
+
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+ synchronized (mUidObserverLock) {
+ if (ActivityManager.isProcStateBackground(procState)) {
+ disableGameMode(uid);
+ return;
+ }
+
+ final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages == null || packages.length == 0) {
+ return;
+ }
+
+ final int userId = mContext.getUserId();
+ if (!Arrays.stream(packages).anyMatch(p -> isPackageGame(p, userId))) {
+ return;
+ }
+
+ if (mForegroundGameUids.isEmpty()) {
+ Slog.v(TAG, "Game power mode ON (process state was changed to foreground)");
+ mPowerManagerInternal.setPowerMode(Mode.GAME, true);
+ }
+ mForegroundGameUids.add(uid);
+ }
+ }
+
+ private void disableGameMode(int uid) {
+ synchronized (mUidObserverLock) {
+ if (!mForegroundGameUids.contains(uid)) {
+ return;
+ }
+ mForegroundGameUids.remove(uid);
+ if (!mForegroundGameUids.isEmpty()) {
+ return;
+ }
+ Slog.v(TAG,
+ "Game power mode OFF (process remomved or state changed to background)");
+ mPowerManagerInternal.setPowerMode(Mode.GAME, false);
+ }
+ }
+
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {}
+
+ @Override
+ public void onUidProcAdjChanged(int uid) {}
+ }
}
diff --git a/services/core/java/com/android/server/appop/AppOpsManagerLocal.java b/services/core/java/com/android/server/appop/AppOpsManagerLocal.java
new file mode 100644
index 0000000..a665896
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsManagerLocal.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appop;
+
+import android.annotation.SystemApi;
+
+/**
+ * In-process app ops API for mainline modules.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface AppOpsManagerLocal {
+
+ /**
+ * Determines if the UID is in foreground in the same way as how foreground runtime
+ * permissions work.
+ *
+ * @return Returns {@code true} if the given UID is in the foreground.
+ */
+ boolean isUidInForeground(int uid);
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 9c6cae3..c50f2b7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -978,6 +978,7 @@
public void publish() {
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal);
+ LocalServices.addService(AppOpsManagerLocal.class, new AppOpsManagerLocalImpl());
}
/** Handler for work when packages are removed or updated */
@@ -6138,6 +6139,15 @@
}
}
+ private final class AppOpsManagerLocalImpl implements AppOpsManagerLocal {
+ @Override
+ public boolean isUidInForeground(int uid) {
+ synchronized (AppOpsService.this) {
+ return mUidStateTracker.isUidInForeground(uid);
+ }
+ }
+ }
+
private final class AppOpsManagerInternalImpl extends AppOpsManagerInternal {
@Override public void setDeviceAndProfileOwners(SparseIntArray owners) {
synchronized (AppOpsService.this) {
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
index 742bf4b..18ea8cf 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
@@ -89,6 +89,11 @@
int getUidState(int uid);
/**
+ * Determines if the uid is in foreground.
+ */
+ boolean isUidInForeground(int uid);
+
+ /**
* Given a uid, code, and mode, resolve any foregroundness to MODE_IGNORED or MODE_ALLOWED
*/
int evalMode(int uid, int code, int mode);
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 5114bd5..49279d4 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -24,8 +24,10 @@
import static android.app.ActivityManager.ProcessCapability;
import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
@@ -124,60 +126,32 @@
@Override
public int evalMode(int uid, int code, int mode) {
- if (mode != AppOpsManager.MODE_FOREGROUND) {
+ if (mode != MODE_FOREGROUND) {
return mode;
}
- int uidStateValue;
- int capability;
- boolean visibleAppWidget;
- boolean pendingTop;
- boolean tempAllowlist;
- uidStateValue = getUidState(uid);
- capability = getUidCapability(uid);
- visibleAppWidget = getUidVisibleAppWidget(uid);
- pendingTop = mActivityManagerInternal.isPendingTopUid(uid);
- tempAllowlist = mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid);
+ int uidState = getUidState(uid);
+ int uidCapability = getUidCapability(uid);
+ int result = evalModeInternal(uid, code, uidState, uidCapability);
- int result = evalMode(uidStateValue, code, mode, capability, visibleAppWidget, pendingTop,
- tempAllowlist);
- mEventLog.logEvalForegroundMode(uid, uidStateValue, capability, code, result);
+ mEventLog.logEvalForegroundMode(uid, uidState, uidCapability, code, result);
return result;
}
- private static int evalMode(int uidState, int code, int mode, int capability,
- boolean appWidgetVisible, boolean pendingTop, boolean tempAllowlist) {
- if (mode != AppOpsManager.MODE_FOREGROUND) {
- return mode;
- }
+ private int evalModeInternal(int uid, int code, int uidState, int uidCapability) {
- if (appWidgetVisible || pendingTop || tempAllowlist) {
+ if (getUidVisibleAppWidget(uid) || mActivityManagerInternal.isPendingTopUid(uid)
+ || mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid)) {
return MODE_ALLOWED;
}
- switch (code) {
- case AppOpsManager.OP_FINE_LOCATION:
- case AppOpsManager.OP_COARSE_LOCATION:
- case AppOpsManager.OP_MONITOR_LOCATION:
- case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_LOCATION) == 0) {
- return MODE_IGNORED;
- } else {
- return MODE_ALLOWED;
- }
- case OP_CAMERA:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) == 0) {
- return MODE_IGNORED;
- } else {
- return MODE_ALLOWED;
- }
- case OP_RECORD_AUDIO:
- case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) == 0) {
- return MODE_IGNORED;
- } else {
- return MODE_ALLOWED;
- }
+ int opCapability = getOpCapability(code);
+ if (opCapability != PROCESS_CAPABILITY_NONE) {
+ if ((uidCapability & opCapability) == 0) {
+ return MODE_IGNORED;
+ } else {
+ return MODE_ALLOWED;
+ }
}
if (uidState > AppOpsManager.resolveFirstUnrestrictedUidState(code)) {
@@ -187,6 +161,28 @@
return MODE_ALLOWED;
}
+ private int getOpCapability(int opCode) {
+ switch (opCode) {
+ case AppOpsManager.OP_FINE_LOCATION:
+ case AppOpsManager.OP_COARSE_LOCATION:
+ case AppOpsManager.OP_MONITOR_LOCATION:
+ case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
+ return PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+ case OP_CAMERA:
+ return PROCESS_CAPABILITY_FOREGROUND_CAMERA;
+ case OP_RECORD_AUDIO:
+ case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
+ return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ default:
+ return PROCESS_CAPABILITY_NONE;
+ }
+ }
+
+ @Override
+ public boolean isUidInForeground(int uid) {
+ return evalMode(uid, OP_NONE, MODE_FOREGROUND) == MODE_ALLOWED;
+ }
+
@Override
public void addUidStateChangedCallback(Executor executor, UidStateChangedCallback callback) {
if (mUidStateChangedCallbacks.containsKey(callback)) {
diff --git a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl b/services/core/java/com/android/server/appop/package-info.java
similarity index 66%
rename from core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
rename to services/core/java/com/android/server/appop/package-info.java
index b30a12a..3b8fb9d 100644
--- a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
+++ b/services/core/java/com/android/server/appop/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,9 @@
* limitations under the License.
*/
-package android.credentials;
-
/**
- * Listener for an registerCredentialDescription request.
- *
* @hide
+ * TODO(b/146466118) remove this javadoc tag
*/
-interface IUnregisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+@android.annotation.Hide
+package com.android.server.appop;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 21bd7bc..b001f3d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -58,6 +58,7 @@
import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
@@ -453,6 +454,48 @@
return device;
}
+ private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+ AudioDeviceInfo.TYPE_WIRED_HEADSET,
+ AudioDeviceInfo.TYPE_USB_HEADSET,
+ AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ AudioDeviceInfo.TYPE_USB_DEVICE,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ AudioDeviceInfo.TYPE_LINE_ANALOG,
+ AudioDeviceInfo.TYPE_HDMI,
+ AudioDeviceInfo.TYPE_AUX_LINE
+ };
+
+ /*package */ static boolean isValidCommunicationDevice(AudioDeviceInfo device) {
+ for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
+ if (device.getType() == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* package */ static List<AudioDeviceInfo> getAvailableCommunicationDevices() {
+ ArrayList<AudioDeviceInfo> commDevices = new ArrayList<>();
+ AudioDeviceInfo[] allDevices =
+ AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
+ for (AudioDeviceInfo device : allDevices) {
+ if (isValidCommunicationDevice(device)) {
+ commDevices.add(device);
+ }
+ }
+ return commDevices;
+ }
+
+ private @Nullable AudioDeviceInfo getCommunicationDeviceOfType(int type) {
+ return getAvailableCommunicationDevices().stream().filter(d -> d.getType() == type)
+ .findFirst().orElse(null);
+ }
+
/**
* Returns the device currently requested for communication use case.
* @return AudioDeviceInfo the requested device for communication.
@@ -460,7 +503,29 @@
/* package */ AudioDeviceInfo getCommunicationDevice() {
synchronized (mDeviceStateLock) {
updateActiveCommunicationDevice();
- return mActiveCommunicationDevice;
+ AudioDeviceInfo device = mActiveCommunicationDevice;
+ // make sure we return a valid communication device (i.e. a device that is allowed by
+ // setCommunicationDevice()) for consistency.
+ if (device != null) {
+ // a digital dock is used instead of the speaker in speakerphone mode and should
+ // be reflected as such
+ if (device.getType() == AudioDeviceInfo.TYPE_DOCK) {
+ device = getCommunicationDeviceOfType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+ }
+ }
+ // Try to default to earpiece when current communication device is not valid. This can
+ // happen for instance if no call is active. If no earpiece device is available take the
+ // first valid communication device
+ if (device == null || !AudioDeviceBroker.isValidCommunicationDevice(device)) {
+ device = getCommunicationDeviceOfType(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
+ if (device == null) {
+ List<AudioDeviceInfo> commDevices = getAvailableCommunicationDevices();
+ if (!commDevices.isEmpty()) {
+ device = commDevices.get(0);
+ }
+ }
+ }
+ return device;
}
}
@@ -942,8 +1007,8 @@
@GuardedBy("mDeviceStateLock")
private void dispatchCommunicationDevice() {
- int portId = (mActiveCommunicationDevice == null) ? 0
- : mActiveCommunicationDevice.getId();
+ AudioDeviceInfo device = getCommunicationDevice();
+ int portId = device != null ? device.getId() : 0;
if (portId == mCurCommunicationPortId) {
return;
}
@@ -960,6 +1025,7 @@
mCommDevDispatchers.finishBroadcast();
}
+
//---------------------------------------------------------------------
// Communication with (to) AudioService
//TODO check whether the AudioService methods are candidates to move here
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 92a9f46..b4992d7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1265,6 +1265,20 @@
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
}
+ private void initVolumeStreamStates() {
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ synchronized (VolumeStreamState.class) {
+ for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+ VolumeStreamState streamState = mStreamStates[streamType];
+ int groupId = getVolumeGroupForStreamType(streamType);
+ if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
+ && sVolumeGroupStates.indexOfKey(groupId) >= 0) {
+ streamState.setVolumeGroupState(sVolumeGroupStates.get(groupId));
+ }
+ }
+ }
+ }
+
/**
* Separating notification volume from ring is NOT of aliasing the corresponding streams
* @param properties
@@ -1292,6 +1306,8 @@
initVolumeGroupStates();
mSoundDoseHelper.initSafeUsbMediaVolumeIndex();
+ // Link VGS on VSS
+ initVolumeStreamStates();
// Call setRingerModeInt() to apply correct mute
// state on streams affected by ringer mode.
@@ -2610,13 +2626,11 @@
}
if (!TextUtils.isEmpty(packageName)) {
PackageManager pm = mContext.getPackageManager();
- ActivityManager am =
- (ActivityManager) mContext.getSystemService(mContext.ACTIVITY_SERVICE);
if (pm.checkPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName)
== PackageManager.PERMISSION_GRANTED) {
try {
- assistantUid = pm.getPackageUidAsUser(packageName, am.getCurrentUser());
+ assistantUid = pm.getPackageUidAsUser(packageName, getCurrentUserId());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG,
"updateAssistantUId() could not find UID for package: " + packageName);
@@ -3482,15 +3496,7 @@
} else {
state = direction == AudioManager.ADJUST_MUTE;
}
- for (int stream = 0; stream < mStreamStates.length; stream++) {
- if (streamTypeAlias == mStreamVolumeAlias[stream]) {
- if (!(readCameraSoundForced()
- && (mStreamStates[stream].getStreamType()
- == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
- mStreamStates[stream].mute(state);
- }
- }
- }
+ muteAliasStreams(streamTypeAlias, state);
} else if ((direction == AudioManager.ADJUST_RAISE)
&& mSoundDoseHelper.raiseVolumeDisplaySafeMediaVolume(streamTypeAlias,
aliasIndex + step, device, flags)) {
@@ -3505,7 +3511,7 @@
// Unmute the stream if it was previously muted
if (direction == AudioManager.ADJUST_RAISE) {
// unmute immediately for volume up
- streamState.mute(false);
+ muteAliasStreams(streamTypeAlias, false);
} else if (direction == AudioManager.ADJUST_LOWER) {
if (mIsSingleVolume) {
sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
@@ -3631,6 +3637,42 @@
sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
}
+ /**
+ * Loops on aliasted stream, update the mute cache attribute of each
+ * {@see AudioService#VolumeStreamState}, and then apply the change.
+ * It prevents to unnecessary {@see AudioSystem#setStreamVolume} done for each stream
+ * and aliases before mute change changed and after.
+ */
+ private void muteAliasStreams(int streamAlias, boolean state) {
+ synchronized (VolumeStreamState.class) {
+ List<Integer> streamsToMute = new ArrayList<>();
+ for (int stream = 0; stream < mStreamStates.length; stream++) {
+ if (streamAlias == mStreamVolumeAlias[stream]) {
+ if (!(readCameraSoundForced()
+ && (mStreamStates[stream].getStreamType()
+ == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
+ boolean changed = mStreamStates[stream].mute(state, /* apply= */ false);
+ if (changed) {
+ streamsToMute.add(stream);
+ }
+ }
+ }
+ }
+ streamsToMute.forEach(streamToMute -> {
+ mStreamStates[streamToMute].doMute();
+ broadcastMuteSetting(streamToMute, state);
+ });
+ }
+ }
+
+ private void broadcastMuteSetting(int streamType, boolean isMuted) {
+ // Stream mute changed, fire the intent.
+ Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+ intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
+ intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, isMuted);
+ sendBroadcastToAll(intent, null /* options */);
+ }
+
// Called after a delay when volume down is pressed while muted
private void onUnmuteStream(int stream, int flags) {
boolean wasMuted;
@@ -3730,7 +3772,8 @@
// except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
if ((streamType != AudioSystem.STREAM_BLUETOOTH_SCO)
&& (getDeviceForStream(stream) == device)) {
- mStreamStates[stream].mute(index == 0);
+ // As adjustStreamVolume with muteAdjust flags mute/unmutes stream and aliased streams.
+ muteAliasStreams(stream, index == 0);
}
}
@@ -3774,23 +3817,23 @@
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- /** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */
- public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
+ @Override
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ /** @see AudioManager#setVolumeGroupVolumeIndex(int, int, int) */
+ public void setVolumeGroupVolumeIndex(int groupId, int index, int flags,
String callingPackage, String attributionTag) {
- super.setVolumeIndexForAttributes_enforcePermission();
-
- Objects.requireNonNull(attr, "attr must not be null");
- int volumeGroup = AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
- attr, /* fallbackOnDefault= */false);
- if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
- Log.e(TAG, ": no volume group found for attributes " + attr.toString());
+ super.setVolumeGroupVolumeIndex_enforcePermission();
+ if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+ Log.e(TAG, ": no volume group found for id " + groupId);
return;
}
- VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+ VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
- sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
- index, flags, callingPackage + ", user " + ActivityManager.getCurrentUser()));
+ sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, vgs.name(),
+ index, flags, callingPackage + ", user " + getCurrentUserId()));
vgs.setVolumeIndex(index, flags);
@@ -3799,7 +3842,7 @@
try {
ensureValidStreamType(groupedStream);
} catch (IllegalArgumentException e) {
- Log.d(TAG, "volume group " + volumeGroup + " has internal streams (" + groupedStream
+ Log.d(TAG, "volume group " + groupId + " has internal streams (" + groupedStream
+ "), do not change associated stream volume");
continue;
}
@@ -3821,39 +3864,55 @@
return null;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- /** @see AudioManager#getVolumeIndexForAttributes(attr) */
- public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
- super.getVolumeIndexForAttributes_enforcePermission();
-
- Objects.requireNonNull(attr, "attr must not be null");
+ @Override
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ /** @see AudioManager#getVolumeGroupVolumeIndex(int) */
+ public int getVolumeGroupVolumeIndex(int groupId) {
+ super.getVolumeGroupVolumeIndex_enforcePermission();
synchronized (VolumeStreamState.class) {
- int volumeGroup = AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
- attr, /* fallbackOnDefault= */false);
- if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
- throw new IllegalArgumentException("No volume group for attributes " + attr);
+ if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+ throw new IllegalArgumentException("No volume group for id " + groupId);
}
- VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
- return vgs.isMuted() ? vgs.getMinIndex() : vgs.getVolumeIndex();
+ VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+ // Return 0 when muted, not min index since for e.g. Voice Call, it has a non zero
+ // min but it mutable on permission condition.
+ return vgs.isMuted() ? 0 : vgs.getVolumeIndex();
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */
- public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
- super.getMaxVolumeIndexForAttributes_enforcePermission();
-
- Objects.requireNonNull(attr, "attr must not be null");
- return AudioSystem.getMaxVolumeIndexForAttributes(attr);
+ /** @see AudioManager#getVolumeGroupMaxVolumeIndex(int) */
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ public int getVolumeGroupMaxVolumeIndex(int groupId) {
+ super.getVolumeGroupMaxVolumeIndex_enforcePermission();
+ synchronized (VolumeStreamState.class) {
+ if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+ throw new IllegalArgumentException("No volume group for id " + groupId);
+ }
+ VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+ return vgs.getMaxIndex();
+ }
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- /** @see AudioManager#getMinVolumeIndexForAttributes(attr) */
- public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
- super.getMinVolumeIndexForAttributes_enforcePermission();
-
- Objects.requireNonNull(attr, "attr must not be null");
- return AudioSystem.getMinVolumeIndexForAttributes(attr);
+ /** @see AudioManager#getVolumeGroupMinVolumeIndex(int) */
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
+ public int getVolumeGroupMinVolumeIndex(int groupId) {
+ super.getVolumeGroupMinVolumeIndex_enforcePermission();
+ synchronized (VolumeStreamState.class) {
+ if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+ throw new IllegalArgumentException("No volume group for id " + groupId);
+ }
+ VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+ return vgs.getMinIndex();
+ }
}
@Override
@@ -3922,6 +3981,71 @@
callingPackage, /*attributionTag*/ null);
}
+ /** @see AudioManager#adjustVolumeGroupVolume(int, int, int) */
+ public void adjustVolumeGroupVolume(int groupId, int direction, int flags,
+ String callingPackage) {
+ ensureValidDirection(direction);
+ if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+ Log.e(TAG, ": no volume group found for id " + groupId);
+ return;
+ }
+ VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+ // For compatibility reason, use stream API if group linked to a valid stream
+ boolean fallbackOnStream = false;
+ for (int stream : vgs.getLegacyStreamTypes()) {
+ try {
+ ensureValidStreamType(stream);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "volume group " + groupId + " has internal streams (" + stream
+ + "), do not change associated stream volume");
+ continue;
+ }
+ // Note: Group and Stream does not share same convention, 0 is mute for stream,
+ // min index is acting as mute for Groups
+ if (vgs.isVssMuteBijective(stream)) {
+ adjustStreamVolume(stream, direction, flags, callingPackage);
+ if (isMuteAdjust(direction)) {
+ // will be propagated to all aliased streams
+ return;
+ }
+ fallbackOnStream = true;
+ }
+ }
+ if (fallbackOnStream) {
+ // Handled by at least one stream, will be propagated to group, bailing out.
+ return;
+ }
+ sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_ADJUST_GROUP_VOL, vgs.name(),
+ direction, flags, callingPackage));
+ vgs.adjustVolume(direction, flags);
+ }
+
+ /** @see AudioManager#getLastAudibleVolumeGroupVolume(int) */
+ @android.annotation.EnforcePermission(android.Manifest.permission.QUERY_AUDIO_STATE)
+ public int getLastAudibleVolumeGroupVolume(int groupId) {
+ super.getLastAudibleVolumeGroupVolume_enforcePermission();
+ synchronized (VolumeStreamState.class) {
+ if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+ Log.e(TAG, ": no volume group found for id " + groupId);
+ return 0;
+ }
+ VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+ return vgs.getVolumeIndex();
+ }
+ }
+
+ /** @see AudioManager#isVolumeGroupMuted(int) */
+ public boolean isVolumeGroupMuted(int groupId) {
+ synchronized (VolumeStreamState.class) {
+ if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+ Log.e(TAG, ": no volume group found for id " + groupId);
+ return false;
+ }
+ VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+ return vgs.isMuted();
+ }
+ }
+
/** @see AudioManager#setStreamVolume(int, int, int)
* Part of service interface, check permissions here */
public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
@@ -5110,7 +5234,7 @@
}
private void setRingerMode(int ringerMode, String caller, boolean external) {
- if (mUseFixedVolume || mIsSingleVolume) {
+ if (mUseFixedVolume || mIsSingleVolume || mUseVolumeGroupAliases) {
return;
}
if (caller == null || caller.length() == 0) {
@@ -5943,49 +6067,16 @@
restoreDeviceVolumeBehavior();
}
- private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
- AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
- AudioDeviceInfo.TYPE_WIRED_HEADSET,
- AudioDeviceInfo.TYPE_USB_HEADSET,
- AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
- AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
- AudioDeviceInfo.TYPE_HEARING_AID,
- AudioDeviceInfo.TYPE_BLE_HEADSET,
- AudioDeviceInfo.TYPE_USB_DEVICE,
- AudioDeviceInfo.TYPE_BLE_SPEAKER,
- AudioDeviceInfo.TYPE_LINE_ANALOG,
- AudioDeviceInfo.TYPE_HDMI,
- AudioDeviceInfo.TYPE_AUX_LINE
- };
-
- private boolean isValidCommunicationDevice(AudioDeviceInfo device) {
- if (!device.isSink()) {
- return false;
- }
- for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
- if (device.getType() == type) {
- return true;
- }
- }
- return false;
- }
-
/** @see AudioManager#getAvailableCommunicationDevices(int) */
public int[] getAvailableCommunicationDeviceIds() {
- ArrayList<Integer> deviceIds = new ArrayList<>();
- AudioDeviceInfo[] devices = AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
- for (AudioDeviceInfo device : devices) {
- if (isValidCommunicationDevice(device)) {
- deviceIds.add(device.getId());
- }
- }
- return deviceIds.stream().mapToInt(Integer::intValue).toArray();
+ List<AudioDeviceInfo> commDevices = AudioDeviceBroker.getAvailableCommunicationDevices();
+ return commDevices.stream().mapToInt(AudioDeviceInfo::getId).toArray();
}
- /**
- * @see AudioManager#setCommunicationDevice(int)
- * @see AudioManager#clearCommunicationDevice()
- */
+
+ /**
+ * @see AudioManager#setCommunicationDevice(int)
+ * @see AudioManager#clearCommunicationDevice()
+ */
public boolean setCommunicationDevice(IBinder cb, int portId) {
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
@@ -5994,9 +6085,10 @@
if (portId != 0) {
device = AudioManager.getDeviceForPortId(portId, AudioManager.GET_DEVICES_OUTPUTS);
if (device == null) {
- throw new IllegalArgumentException("invalid portID " + portId);
+ Log.w(TAG, "setCommunicationDevice: invalid portID " + portId);
+ return false;
}
- if (!isValidCommunicationDevice(device)) {
+ if (!AudioDeviceBroker.isValidCommunicationDevice(device)) {
if (!device.isSink()) {
throw new IllegalArgumentException("device must have sink role");
} else {
@@ -6044,17 +6136,15 @@
/** @see AudioManager#getCommunicationDevice() */
public int getCommunicationDevice() {
- AudioDeviceInfo device = null;
+ int deviceId = 0;
final long ident = Binder.clearCallingIdentity();
try {
- device = mDeviceBroker.getCommunicationDevice();
+ AudioDeviceInfo device = mDeviceBroker.getCommunicationDevice();
+ deviceId = device != null ? device.getId() : 0;
} finally {
Binder.restoreCallingIdentity(ident);
}
- if (device == null) {
- return 0;
- }
- return device.getId();
+ return deviceId;
}
/** @see AudioManager#addOnCommunicationDeviceChangedListener(
@@ -7006,20 +7096,21 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
/**
* @see AudioManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)
* @param device the audio device to be affected
* @param deviceVolumeBehavior one of the device behaviors
*/
+ @android.annotation.EnforcePermission(anyOf =
+ {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SYSTEM_SETTINGS"})
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
// verify permissions
- // verify arguments
super.setDeviceVolumeBehavior_enforcePermission();
-
+ // verify arguments
Objects.requireNonNull(device);
AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
+
sVolumeLogger.enqueue(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+ AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
+ device.getAddress() + " behavior:"
@@ -7090,11 +7181,14 @@
* @param device the audio output device type
* @return the volume behavior for the device
*/
+ @android.annotation.EnforcePermission(anyOf =
+ {"MODIFY_AUDIO_ROUTING", "QUERY_AUDIO_STATE", "MODIFY_AUDIO_SYSTEM_SETTINGS"})
public @AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
- Objects.requireNonNull(device);
// verify permissions
- enforceQueryStateOrModifyRoutingPermission();
+ super.getDeviceVolumeBehavior_enforcePermission();
+ // verify parameters
+ Objects.requireNonNull(device);
return getDeviceVolumeBehaviorInt(device);
}
@@ -7413,6 +7507,16 @@
|| stream == AudioSystem.STREAM_BLUETOOTH_SCO;
}
+ private static int getVolumeGroupForStreamType(int stream) {
+ AudioAttributes attributes =
+ AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(stream);
+ if (attributes.equals(new AudioAttributes.Builder().build())) {
+ return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
+ }
+ return AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
+ attributes, /* fallbackOnDefault= */ false);
+ }
+
// NOTE: Locking order for synchronized objects related to volume management:
// 1 mSettingsLock
// 2 VolumeStreamState.class
@@ -7521,14 +7625,62 @@
return mIsMuted;
}
+ public void adjustVolume(int direction, int flags) {
+ synchronized (AudioService.VolumeStreamState.class) {
+ int device = getDeviceForVolume();
+ int previousIndex = getIndex(device);
+ if (isMuteAdjust(direction) && !isMutable()) {
+ // Non mutable volume group
+ if (DEBUG_VOL) {
+ Log.d(TAG, "invalid mute on unmutable volume group " + name());
+ }
+ return;
+ }
+ switch (direction) {
+ case AudioManager.ADJUST_TOGGLE_MUTE: {
+ // Note: If muted by volume 0, unmute will restore volume 0.
+ mute(!mIsMuted);
+ break;
+ }
+ case AudioManager.ADJUST_UNMUTE:
+ // Note: If muted by volume 0, unmute will restore volume 0.
+ mute(false);
+ break;
+ case AudioManager.ADJUST_MUTE:
+ // May be already muted by setvolume 0, prevent from setting same value
+ if (previousIndex != 0) {
+ // bypass persist
+ mute(true);
+ }
+ mIsMuted = true;
+ break;
+ case AudioManager.ADJUST_RAISE:
+ // As for stream, RAISE during mute will increment the index
+ setVolumeIndex(Math.min(previousIndex + 1, mIndexMax), device, flags);
+ break;
+ case AudioManager.ADJUST_LOWER:
+ // For stream, ADJUST_LOWER on a muted VSS is a no-op
+ // If we decide to unmute on ADJUST_LOWER, cannot fallback on
+ // adjustStreamVolume for group associated to legacy stream type
+ if (isMuted() && previousIndex != 0) {
+ mute(false);
+ } else {
+ int newIndex = Math.max(previousIndex - 1, mIndexMin);
+ setVolumeIndex(newIndex, device, flags);
+ }
+ break;
+ }
+ }
+ }
+
public int getVolumeIndex() {
- synchronized (VolumeStreamState.class) {
+ synchronized (AudioService.VolumeStreamState.class) {
return getIndex(getDeviceForVolume());
}
}
public void setVolumeIndex(int index, int flags) {
- synchronized (VolumeStreamState.class) {
+ synchronized (AudioService.VolumeStreamState.class) {
if (mUseFixedVolume) {
return;
}
@@ -7613,7 +7765,7 @@
public void applyAllVolumes(boolean userSwitch) {
String caller = "from vgs";
- synchronized (VolumeStreamState.class) {
+ synchronized (AudioService.VolumeStreamState.class) {
// apply device specific volumes first
for (int i = 0; i < mIndexMap.size(); i++) {
int device = mIndexMap.keyAt(i);
@@ -7713,7 +7865,7 @@
Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
+ mAudioVolumeGroup.name()
+ ", device " + AudioSystem.getOutputDeviceName(device)
- + " and User=" + ActivityManager.getCurrentUser());
+ + " and User=" + getCurrentUserId());
}
boolean success = mSettings.putSystemIntForUser(mContentResolver,
getSettingNameForDevice(device),
@@ -7725,7 +7877,7 @@
}
public void readSettings() {
- synchronized (VolumeStreamState.class) {
+ synchronized (AudioService.VolumeStreamState.class) {
// force maximum volume on all streams if fixed volume property is set
if (mUseFixedVolume) {
mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -7752,7 +7904,7 @@
if (DEBUG_VOL) {
Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+ " for group " + mAudioVolumeGroup.name() + ", device: " + name
- + ", User=" + ActivityManager.getCurrentUser());
+ + ", User=" + getCurrentUserId());
}
mIndexMap.put(device, getValidIndex(index));
}
@@ -7829,13 +7981,14 @@
// 4 VolumeStreamState.class
/*package*/ class VolumeStreamState {
private final int mStreamType;
+ private VolumeGroupState mVolumeGroupState = null;
private int mIndexMin;
// min index when user doesn't have permission to change audio settings
private int mIndexMinNoPerm;
private int mIndexMax;
- private boolean mIsMuted;
- private boolean mIsMutedInternally;
+ private boolean mIsMuted = false;
+ private boolean mIsMutedInternally = false;
private String mVolumeIndexSettingName;
@NonNull private Set<Integer> mObservedDeviceSet = new TreeSet<>();
@@ -7896,6 +8049,7 @@
volumeChangedOptions.setDeliveryGroupPolicy(DELIVERY_GROUP_POLICY_MOST_RECENT);
volumeChangedOptions.setDeliveryGroupMatchingKey(
AudioManager.VOLUME_CHANGED_ACTION, String.valueOf(mStreamType));
+ volumeChangedOptions.setDeferUntilActive(true);
mVolumeChangedOptions = volumeChangedOptions.toBundle();
mStreamDevicesChanged = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
@@ -7904,10 +8058,20 @@
streamDevicesChangedOptions.setDeliveryGroupPolicy(DELIVERY_GROUP_POLICY_MOST_RECENT);
streamDevicesChangedOptions.setDeliveryGroupMatchingKey(
AudioManager.STREAM_DEVICES_CHANGED_ACTION, String.valueOf(mStreamType));
+ streamDevicesChangedOptions.setDeferUntilActive(true);
mStreamDevicesChangedOptions = streamDevicesChangedOptions.toBundle();
}
/**
+ * Associate a {@link volumeGroupState} on the {@link VolumeStreamState}.
+ * <p> It helps to synchronize the index, mute attributes on the maching
+ * {@link volumeGroupState}
+ * @param volumeGroupState matching the {@link VolumeStreamState}
+ */
+ public void setVolumeGroupState(VolumeGroupState volumeGroupState) {
+ mVolumeGroupState = volumeGroupState;
+ }
+ /**
* Update the minimum index that can be used without MODIFY_AUDIO_SETTINGS permission
* @param index minimum index expressed in "UI units", i.e. no 10x factor
*/
@@ -8168,6 +8332,9 @@
}
}
if (changed) {
+ // If associated to volume group, update group cache
+ updateVolumeGroupIndex(device, /* forceMuteState= */ false);
+
oldIndex = (oldIndex + 5) / 10;
index = (index + 5) / 10;
// log base stream changes to the event log
@@ -8285,6 +8452,28 @@
}
}
+ // If associated to volume group, update group cache
+ private void updateVolumeGroupIndex(int device, boolean forceMuteState) {
+ synchronized (VolumeStreamState.class) {
+ if (mVolumeGroupState != null) {
+ int groupIndex = (getIndex(device) + 5) / 10;
+ if (DEBUG_VOL) {
+ Log.d(TAG, "updateVolumeGroupIndex for stream " + mStreamType
+ + ", muted=" + mIsMuted + ", device=" + device + ", index="
+ + getIndex(device) + ", group " + mVolumeGroupState.name()
+ + " Muted=" + mVolumeGroupState.isMuted() + ", Index=" + groupIndex
+ + ", forceMuteState=" + forceMuteState);
+ }
+ mVolumeGroupState.updateVolumeIndex(groupIndex, device);
+ // Only propage mute of stream when applicable
+ if (mIndexMin == 0 || isCallStream(mStreamType)) {
+ // For call stream, align mute only when muted, not when index is set to 0
+ mVolumeGroupState.mute(forceMuteState ? mIsMuted : groupIndex == 0);
+ }
+ }
+ }
+ }
+
/**
* Mute/unmute the stream
* @param state the new mute state
@@ -8293,27 +8482,10 @@
public boolean mute(boolean state) {
boolean changed = false;
synchronized (VolumeStreamState.class) {
- if (state != mIsMuted) {
- changed = true;
- mIsMuted = state;
-
- // Set the new mute volume. This propagates the values to
- // the audio system, otherwise the volume won't be changed
- // at the lower level.
- sendMsg(mAudioHandler,
- MSG_SET_ALL_VOLUMES,
- SENDMSG_QUEUE,
- 0,
- 0,
- this, 0);
- }
+ changed = mute(state, true);
}
if (changed) {
- // Stream mute changed, fire the intent.
- Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
- intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
- intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
- sendBroadcastToAll(intent, null /* options */);
+ broadcastMuteSetting(mStreamType, state);
}
return changed;
}
@@ -8345,6 +8517,44 @@
return mIsMuted || mIsMutedInternally;
}
+ /**
+ * Mute/unmute the stream
+ * @param state the new mute state
+ * @param apply true to propagate to HW, or false just to update the cache. May be needed
+ * to mute a stream and its aliases as applyAllVolume will force settings to aliases.
+ * It prevents unnecessary calls to {@see AudioSystem#setStreamVolume}
+ * @return true if the mute state was changed
+ */
+ public boolean mute(boolean state, boolean apply) {
+ synchronized (VolumeStreamState.class) {
+ boolean changed = state != mIsMuted;
+ if (changed) {
+ mIsMuted = state;
+ if (apply) {
+ doMute();
+ }
+ }
+ return changed;
+ }
+ }
+
+ public void doMute() {
+ synchronized (VolumeStreamState.class) {
+ // If associated to volume group, update group cache
+ updateVolumeGroupIndex(getDeviceForStream(mStreamType), /* forceMuteState= */ true);
+
+ // Set the new mute volume. This propagates the values to
+ // the audio system, otherwise the volume won't be changed
+ // at the lower level.
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ this, 0);
+ }
+ }
+
public int getStreamType() {
return mStreamType;
}
@@ -8414,6 +8624,9 @@
pw.println();
pw.print(" Devices: ");
pw.print(AudioSystem.deviceSetToString(getDeviceSetForStream(mStreamType)));
+ pw.println();
+ pw.print(" Volume Group: ");
+ pw.println(mVolumeGroupState != null ? mVolumeGroupState.name() : "n/a");
}
}
@@ -11164,12 +11377,12 @@
}
try {
- if (!projectionService.isValidMediaProjection(projection)) {
+ if (!projectionService.isCurrentProjection(projection)) {
Log.w(TAG, "App passed invalid MediaProjection token");
return false;
}
} catch (RemoteException e) {
- Log.e(TAG, "Can't call .isValidMediaProjection() on IMediaProjectionManager"
+ Log.e(TAG, "Can't call .isCurrentProjection() on IMediaProjectionManager"
+ projectionService.asBinder(), e);
return false;
}
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index d30bec7..58caf5a 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -17,7 +17,6 @@
package com.android.server.audio;
import android.annotation.NonNull;
-import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -197,6 +196,7 @@
static final int VOL_SET_GROUP_VOL = 8;
static final int VOL_MUTE_STREAM_INT = 9;
static final int VOL_SET_LE_AUDIO_VOL = 10;
+ static final int VOL_ADJUST_GROUP_VOL = 11;
final int mOp;
final int mStream;
@@ -204,7 +204,6 @@
final int mVal2;
final String mCaller;
final String mGroupName;
- final AudioAttributes mAudioAttributes;
/** used for VOL_ADJUST_VOL_UID,
* VOL_ADJUST_SUGG_VOL,
@@ -217,7 +216,6 @@
mVal2 = val2;
mCaller = caller;
mGroupName = null;
- mAudioAttributes = null;
logMetricEvent();
}
@@ -230,7 +228,6 @@
mStream = -1;
mCaller = null;
mGroupName = null;
- mAudioAttributes = null;
logMetricEvent();
}
@@ -243,7 +240,6 @@
mStream = -1;
mCaller = null;
mGroupName = null;
- mAudioAttributes = null;
logMetricEvent();
}
@@ -256,7 +252,6 @@
// unused
mCaller = null;
mGroupName = null;
- mAudioAttributes = null;
logMetricEvent();
}
@@ -269,19 +264,18 @@
// unused
mCaller = null;
mGroupName = null;
- mAudioAttributes = null;
logMetricEvent();
}
- /** used for VOL_SET_GROUP_VOL */
- VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) {
+ /** used for VOL_SET_GROUP_VOL,
+ * VOL_ADJUST_GROUP_VOL */
+ VolumeEvent(int op, String group, int index, int flags, String caller) {
mOp = op;
mStream = -1;
mVal1 = index;
mVal2 = flags;
mCaller = caller;
mGroupName = group;
- mAudioAttributes = aa;
logMetricEvent();
}
@@ -293,7 +287,6 @@
mVal2 = 0;
mCaller = null;
mGroupName = null;
- mAudioAttributes = null;
logMetricEvent();
}
@@ -335,6 +328,15 @@
.record();
return;
}
+ case VOL_ADJUST_GROUP_VOL:
+ new MediaMetrics.Item(mMetricsId)
+ .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
+ .set(MediaMetrics.Property.DIRECTION, mVal1 > 0 ? "up" : "down")
+ .set(MediaMetrics.Property.EVENT, "adjustVolumeGroupVolume")
+ .set(MediaMetrics.Property.FLAGS, mVal2)
+ .set(MediaMetrics.Property.GROUP, mGroupName)
+ .record();
+ return;
case VOL_SET_STREAM_VOL:
new MediaMetrics.Item(mMetricsId)
.set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
@@ -386,7 +388,6 @@
return;
case VOL_SET_GROUP_VOL:
new MediaMetrics.Item(mMetricsId)
- .set(MediaMetrics.Property.ATTRIBUTES, mAudioAttributes.toString())
.set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
.set(MediaMetrics.Property.EVENT, "setVolumeIndexForAttributes")
.set(MediaMetrics.Property.FLAGS, mVal2)
@@ -412,6 +413,13 @@
.append(" flags:0x").append(Integer.toHexString(mVal2))
.append(") from ").append(mCaller)
.toString();
+ case VOL_ADJUST_GROUP_VOL:
+ return new StringBuilder("adjustVolumeGroupVolume(group:")
+ .append(mGroupName)
+ .append(" dir:").append(AudioManager.adjustToString(mVal1))
+ .append(" flags:0x").append(Integer.toHexString(mVal2))
+ .append(") from ").append(mCaller)
+ .toString();
case VOL_ADJUST_STREAM_VOL:
return new StringBuilder("adjustStreamVolume(stream:")
.append(AudioSystem.streamToString(mStream))
@@ -460,8 +468,7 @@
.append(" stream:").append(AudioSystem.streamToString(mStream))
.toString();
case VOL_SET_GROUP_VOL:
- return new StringBuilder("setVolumeIndexForAttributes(attr:")
- .append(mAudioAttributes.toString())
+ return new StringBuilder("setVolumeIndexForAttributes(group:")
.append(" group: ").append(mGroupName)
.append(" index:").append(mVal1)
.append(" flags:0x").append(Integer.toHexString(mVal2))
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index b18be3c..35df3ee 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -86,7 +86,7 @@
private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER,
- ACCOUNT_MANAGER_HELPER);
+ ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER, PREFERRED_HELPER);
private int mUserId = UserHandle.USER_SYSTEM;
@@ -100,7 +100,7 @@
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(mUserId));
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(mUserId));
addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId));
- addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
+ addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(mUserId));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper(mUserId));
addHelper(SLICES_HELPER, new SliceBackupHelper(this));
diff --git a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
index d6a70d3..4098c1a 100644
--- a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
+++ b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
@@ -1,9 +1,9 @@
package com.android.server.backup;
+import android.annotation.UserIdInt;
import android.app.backup.BlobBackupHelper;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.Context;
import android.os.UserHandle;
import android.util.Log;
@@ -26,8 +26,16 @@
// same as UsageStatsDatabase.KEY_USAGE_STATS
static final String KEY_USAGE_STATS = "usage_stats";
- public UsageStatsBackupHelper(Context context) {
+ private final @UserIdInt int mUserId;
+
+ /**
+ * Marshall/unmarshall the usagestats data for the given user
+ *
+ * @param userId The userId to backup/restore
+ */
+ public UsageStatsBackupHelper(@UserIdInt int userId) {
super(BLOB_VERSION, KEY_USAGE_STATS);
+ mUserId = userId;
}
@Override
@@ -38,8 +46,11 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
try {
+ // Note: Write 0 here deliberately so that a backup from a secondary user
+ // can still be restored to an older OS where the restore was always to user 0
+ // Writing the actual userId here would result in restores not working on pre-U.
out.writeInt(UserHandle.USER_SYSTEM);
- out.write(localUsageStatsManager.getBackupPayload(UserHandle.USER_SYSTEM, key));
+ out.write(localUsageStatsManager.getBackupPayload(mUserId, key));
} catch (IOException ioe) {
if (DEBUG) Log.e(TAG, "Failed to backup Usage Stats", ioe);
baos.reset();
@@ -49,7 +60,6 @@
return null;
}
-
@Override
protected void applyRestoredPayload(String key, byte[] payload) {
if (KEY_USAGE_STATS.equals(key)) {
@@ -57,10 +67,10 @@
LocalServices.getService(UsageStatsManagerInternal.class);
DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
try {
- int user = in.readInt();
+ in.readInt(); // Legacy userId parameter, read and ignore
byte[] restoreData = new byte[payload.length - 4];
in.read(restoreData, 0, restoreData.length);
- localUsageStatsManager.applyRestoredPayload(user, key, restoreData);
+ localUsageStatsManager.applyRestoredPayload(mUserId, key, restoreData);
} catch (IOException ioe) {
if (DEBUG) Log.e(TAG, "Failed to restore Usage Stats", ioe);
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 6f9a176..ecff0a7 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -17,6 +17,10 @@
package com.android.server.clipboard;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
+import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
+import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
+import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
import android.Manifest;
import android.annotation.NonNull;
@@ -29,6 +33,8 @@
import android.app.IUriGrantsManager;
import android.app.KeyguardManager;
import android.app.UriGrantsManager;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
@@ -39,11 +45,13 @@
import android.content.IClipboard;
import android.content.IOnPrimaryClipChangedListener;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -64,10 +72,13 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
import android.util.SafetyProtectionUtils;
import android.util.Slog;
-import android.util.SparseArray;
+import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
+import android.view.Display;
import android.view.autofill.AutofillManagerInternal;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
@@ -130,7 +141,9 @@
private final IUriGrantsManager mUgm;
private final UriGrantsManagerInternal mUgmInternal;
private final WindowManagerInternal mWm;
- private final VirtualDeviceManagerInternal mVdm;
+ private final VirtualDeviceManagerInternal mVdmInternal;
+ private final VirtualDeviceManager mVdm;
+ private BroadcastReceiver mVirtualDeviceRemovedReceiver;
private final IUserManager mUm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
@@ -141,11 +154,15 @@
private final Handler mWorkerHandler;
@GuardedBy("mLock")
- private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
+ // Maps (userId, deviceId) to Clipboard.
+ private final SparseArrayMap<Integer, Clipboard> mClipboards = new SparseArrayMap<>();
@GuardedBy("mLock")
private boolean mShowAccessNotifications =
ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+ @GuardedBy("mLock")
+ private boolean mAllowVirtualDeviceSilos =
+ ClipboardManager.DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS;
@GuardedBy("mLock")
private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
@@ -163,7 +180,9 @@
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mWm = LocalServices.getService(WindowManagerInternal.class);
// Can be null; not all products have CDM + VirtualDeviceManager
- mVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
+ mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
+ mVdm = (mVdmInternal == null) ? null : getContext().getSystemService(
+ VirtualDeviceManager.class);
mPm = getContext().getPackageManager();
mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -174,7 +193,7 @@
if (IS_EMULATOR) {
mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
synchronized (mLock) {
- setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
+ setPrimaryClipInternalLocked(getClipboardLocked(0, DEVICE_ID_DEFAULT), clip,
android.os.Process.SYSTEM_UID, null);
}
});
@@ -194,12 +213,39 @@
@Override
public void onStart() {
publishBinderService(Context.CLIPBOARD_SERVICE, new ClipboardImpl());
+ if (mVdmInternal != null) {
+ registerVirtualDeviceRemovedListener();
+ }
+ }
+
+ private void registerVirtualDeviceRemovedListener() {
+ if (mVirtualDeviceRemovedReceiver != null) {
+ return;
+ }
+ mVirtualDeviceRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(ACTION_VIRTUAL_DEVICE_REMOVED)) {
+ return;
+ }
+ final int removedDeviceId =
+ intent.getIntExtra(EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_INVALID);
+ synchronized (mLock) {
+ for (int i = mClipboards.numMaps() - 1; i >= 0; i--) {
+ mClipboards.delete(mClipboards.keyAt(i), removedDeviceId);
+ }
+ }
+ }
+ };
+ IntentFilter filter = new IntentFilter(ACTION_VIRTUAL_DEVICE_REMOVED);
+ getContext().registerReceiver(mVirtualDeviceRemovedReceiver, filter,
+ Context.RECEIVER_NOT_EXPORTED);
}
@Override
public void onUserStopped(@NonNull TargetUser user) {
synchronized (mLock) {
- mClipboards.remove(user.getUserIdentifier());
+ mClipboards.delete(user.getUserIdentifier());
}
}
@@ -209,6 +255,10 @@
DeviceConfig.NAMESPACE_CLIPBOARD,
ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,
ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+ mAllowVirtualDeviceSilos = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CLIPBOARD,
+ ClipboardManager.DEVICE_CONFIG_ALLOW_VIRTUALDEVICE_SILOS,
+ ClipboardManager.DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS);
mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
}
@@ -226,8 +276,9 @@
}
}
- private class PerUserClipboard {
- final int userId;
+ private static class Clipboard {
+ public final int userId;
+ public final int deviceId;
final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners
= new RemoteCallbackList<IOnPrimaryClipChangedListener>();
@@ -254,8 +305,9 @@
/** The text classifier session that is used to annotate the text in the primary clip. */
TextClassifier mTextClassifier;
- PerUserClipboard(int userId) {
+ Clipboard(int userId, int deviceId) {
this.userId = userId;
+ this.deviceId = deviceId;
}
}
@@ -333,6 +385,54 @@
}
/**
+ * Determines which deviceId to use for selecting a Clipboard, depending on where a given app
+ * is running.
+ *
+ * @param requestedDeviceId the requested deviceId passed in from the client side
+ * @param uid the intended app uid
+ * @return a deviceId to use in selecting the appropriate clipboard, or
+ * DEVICE_ID_INVALID if this uid should not be allowed access. A value of DEVICE_ID_DEFAULT
+ * means just use the "regular" clipboard.
+ */
+ private int getIntendingDeviceId(int requestedDeviceId, int uid) {
+ if (mVdmInternal == null) {
+ return DEVICE_ID_DEFAULT;
+ }
+
+ ArraySet<Integer> virtualDeviceIds = mVdmInternal.getDeviceIdsForUid(uid);
+
+ synchronized (mLock) {
+ if (!mAllowVirtualDeviceSilos
+ && (!virtualDeviceIds.isEmpty() || requestedDeviceId != DEVICE_ID_DEFAULT)) {
+ return DEVICE_ID_INVALID;
+ }
+ }
+
+ if (requestedDeviceId != DEVICE_ID_DEFAULT) {
+ // Privileged apps that own the VirtualDevices, or regular apps running on it, can
+ // request it by id.
+ if (mVdmInternal.getDeviceOwnerUid(requestedDeviceId) == uid
+ || virtualDeviceIds.contains(requestedDeviceId)) {
+ return requestedDeviceId;
+ }
+ return DEVICE_ID_INVALID;
+ }
+
+ // The common case is apps running normally (not on a VirtualDevice).
+ if (virtualDeviceIds.isEmpty()) {
+ return DEVICE_ID_DEFAULT;
+ }
+
+ // If an app is running on more than one VirtualDevice, it isn't clear which clipboard they
+ // should use.
+ if (virtualDeviceIds.size() > 1) {
+ return DEVICE_ID_INVALID;
+ }
+
+ return virtualDeviceIds.valueAt(0);
+ }
+
+ /**
* To handle the difference between userId and intendingUserId, uid and intendingUid.
*
* userId means that comes from the calling side and should be validated by
@@ -364,8 +464,10 @@
ClipData clip,
String callingPackage,
String attributionTag,
- @UserIdInt int userId) {
- checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, callingPackage);
+ @UserIdInt int userId,
+ int deviceId) {
+ checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, deviceId,
+ callingPackage);
}
@Override
@@ -374,10 +476,12 @@
String callingPackage,
String attributionTag,
@UserIdInt int userId,
+ int deviceId,
String sourcePackage) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
"Requires SET_CLIP_SOURCE permission");
- checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, sourcePackage);
+ checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, deviceId,
+ sourcePackage);
}
private void checkAndSetPrimaryClip(
@@ -385,41 +489,46 @@
String callingPackage,
String attributionTag,
@UserIdInt int userId,
+ int deviceId,
String sourcePackage) {
if (clip == null || clip.getItemCount() <= 0) {
throw new IllegalArgumentException("No items");
}
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_WRITE_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
- intendingUserId)) {
+ intendingUserId,
+ intendingDeviceId)) {
return;
}
checkDataOwner(clip, intendingUid);
synchronized (mLock) {
- scheduleAutoClear(userId, intendingUid);
- setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);
+ scheduleAutoClear(userId, intendingUid, intendingDeviceId);
+ setPrimaryClipInternalLocked(clip, intendingUid, intendingDeviceId, sourcePackage);
}
}
- private void scheduleAutoClear(@UserIdInt int userId, int intendingUid) {
+ private void scheduleAutoClear(
+ @UserIdInt int userId, int intendingUid, int intendingDeviceId) {
final long oldIdentity = Binder.clearCallingIdentity();
try {
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
PROPERTY_AUTO_CLEAR_ENABLED, true)) {
+ Pair<Integer, Integer> userIdDeviceId = new Pair<>(userId, intendingDeviceId);
mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
- userId);
+ userIdDeviceId);
Message clearMessage =
Message.obtain(
mClipboardClearHandler,
ClipboardClearHandler.MSG_CLEAR,
userId,
intendingUid,
- userId);
+ userIdDeviceId);
mClipboardClearHandler.sendMessageDelayed(clearMessage,
getTimeoutForAutoClear());
}
@@ -436,52 +545,57 @@
@Override
public void clearPrimaryClip(
- String callingPackage, String attributionTag, @UserIdInt int userId) {
+ String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_WRITE_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
- intendingUserId)) {
+ intendingUserId,
+ intendingDeviceId)) {
return;
}
synchronized (mLock) {
mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
- userId);
- setPrimaryClipInternalLocked(null, intendingUid, callingPackage);
+ new Pair<>(userId, deviceId));
+ setPrimaryClipInternalLocked(null, intendingUid, intendingDeviceId, callingPackage);
}
}
@Override
- public ClipData getPrimaryClip(String pkg, String attributionTag, @UserIdInt int userId) {
+ public ClipData getPrimaryClip(
+ String pkg, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(pkg, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
pkg,
attributionTag,
intendingUid,
- intendingUserId)
+ intendingUserId,
+ intendingDeviceId)
|| isDeviceLocked(intendingUserId)) {
return null;
}
synchronized (mLock) {
try {
- addActiveOwnerLocked(intendingUid, pkg);
+ addActiveOwnerLocked(intendingUid, intendingDeviceId, pkg);
} catch (SecurityException e) {
// Permission could not be granted - URI may be invalid
Slog.i(TAG, "Could not grant permission to primary clip. Clearing clipboard.");
- setPrimaryClipInternalLocked(null, intendingUid, pkg);
+ setPrimaryClipInternalLocked(null, intendingUid, intendingDeviceId, pkg);
return null;
}
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
if (clipboard.primaryClip != null) {
- scheduleAutoClear(userId, intendingUid);
+ scheduleAutoClear(userId, intendingUid, intendingDeviceId);
}
return clipboard.primaryClip;
}
@@ -489,21 +603,23 @@
@Override
public ClipDescription getPrimaryClipDescription(
- String callingPackage, String attributionTag, @UserIdInt int userId) {
+ String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
return clipboard.primaryClip != null
? clipboard.primaryClip.getDescription() : null;
}
@@ -511,21 +627,23 @@
@Override
public boolean hasPrimaryClip(
- String callingPackage, String attributionTag, @UserIdInt int userId) {
+ String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
synchronized (mLock) {
- return getClipboardLocked(intendingUserId).primaryClip != null;
+ return getClipboardLocked(intendingUserId, intendingDeviceId).primaryClip != null;
}
}
@@ -534,11 +652,19 @@
IOnPrimaryClipChangedListener listener,
String callingPackage,
String attributionTag,
- @UserIdInt int userId) {
+ @UserIdInt int userId,
+ int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
+ if (intendingDeviceId == DEVICE_ID_INVALID) {
+ Slog.i(TAG, "addPrimaryClipChangedListener invalid deviceId for userId:"
+ + userId + " uid:" + intendingUid + " callingPackage:" + callingPackage
+ + " requestedDeviceId:" + deviceId);
+ return;
+ }
synchronized (mLock) {
- getClipboardLocked(intendingUserId)
+ getClipboardLocked(intendingUserId, intendingDeviceId)
.primaryClipListeners
.register(
listener,
@@ -551,29 +677,41 @@
IOnPrimaryClipChangedListener listener,
String callingPackage,
String attributionTag,
- @UserIdInt int userId) {
+ @UserIdInt int userId,
+ int deviceId) {
+ final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = getIntendingUserId(callingPackage, userId);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
+ if (intendingDeviceId == DEVICE_ID_INVALID) {
+ Slog.i(TAG, "removePrimaryClipChangedListener invalid deviceId for userId:"
+ + userId + " uid:" + intendingUid + " callingPackage:" + callingPackage);
+ return;
+ }
synchronized (mLock) {
- getClipboardLocked(intendingUserId).primaryClipListeners.unregister(listener);
+ getClipboardLocked(intendingUserId,
+ intendingDeviceId).primaryClipListeners.unregister(listener);
}
}
@Override
- public boolean hasClipboardText(String callingPackage, String attributionTag, int userId) {
+ public boolean hasClipboardText(
+ String callingPackage, String attributionTag, int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
if (clipboard.primaryClip != null) {
CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
return text != null && text.length() > 0;
@@ -584,23 +722,25 @@
@Override
public String getPrimaryClipSource(
- String callingPackage, String attributionTag, int userId) {
+ String callingPackage, String attributionTag, int userId, int deviceId) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
"Requires SET_CLIP_SOURCE permission");
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
if (clipboard.primaryClip != null) {
return clipboard.mPrimaryClipPackage;
}
@@ -621,11 +761,13 @@
case MSG_CLEAR:
final int userId = msg.arg1;
final int intendingUid = msg.arg2;
+ final int intendingDeviceId = ((Pair<Integer, Integer>) msg.obj).second;
synchronized (mLock) {
- if (getClipboardLocked(userId).primaryClip != null) {
+ if (getClipboardLocked(userId, intendingDeviceId).primaryClip != null) {
FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED,
FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR);
- setPrimaryClipInternalLocked(null, intendingUid, null);
+ setPrimaryClipInternalLocked(
+ null, intendingUid, intendingDeviceId, null);
}
}
break;
@@ -637,13 +779,13 @@
};
@GuardedBy("mLock")
- private PerUserClipboard getClipboardLocked(@UserIdInt int userId) {
- PerUserClipboard puc = mClipboards.get(userId);
- if (puc == null) {
- puc = new PerUserClipboard(userId);
- mClipboards.put(userId, puc);
+ private Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) {
+ Clipboard clipboard = mClipboards.get(userId, deviceId);
+ if (clipboard == null) {
+ clipboard = new Clipboard(userId, deviceId);
+ mClipboards.add(userId, deviceId, clipboard);
}
- return puc;
+ return clipboard;
}
List<UserInfo> getRelatedProfiles(@UserIdInt int userId) {
@@ -675,19 +817,20 @@
void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
synchronized (mLock) {
- setPrimaryClipInternalLocked(clip, uid, null);
+ setPrimaryClipInternalLocked(clip, uid, DEVICE_ID_DEFAULT, null);
}
}
@GuardedBy("mLock")
private void setPrimaryClipInternalLocked(
- @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
+ @Nullable ClipData clip, int uid, int deviceId, @Nullable String sourcePackage) {
mEmulatorClipboardMonitor.accept(clip);
final int userId = UserHandle.getUserId(uid);
// Update this user
- setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
+ setPrimaryClipInternalLocked(getClipboardLocked(userId, deviceId), clip, uid,
+ sourcePackage);
// Update related users
List<UserInfo> related = getRelatedProfiles(userId);
@@ -722,7 +865,7 @@
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
setPrimaryClipInternalNoClassifyLocked(
- getClipboardLocked(id), clip, uid, sourcePackage);
+ getClipboardLocked(id, deviceId), clip, uid, sourcePackage);
}
}
}
@@ -730,7 +873,7 @@
}
}
- void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
+ void setPrimaryClipInternal(Clipboard clipboard, @Nullable ClipData clip,
int uid) {
synchronized (mLock) {
setPrimaryClipInternalLocked(clipboard, clip, uid, null);
@@ -738,18 +881,18 @@
}
@GuardedBy("mLock")
- private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
+ private void setPrimaryClipInternalLocked(Clipboard clipboard, @Nullable ClipData clip,
int uid, @Nullable String sourcePackage) {
final int userId = UserHandle.getUserId(uid);
if (clip != null) {
- startClassificationLocked(clip, userId);
+ startClassificationLocked(clip, userId, clipboard.deviceId);
}
setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
}
@GuardedBy("mLock")
- private void setPrimaryClipInternalNoClassifyLocked(PerUserClipboard clipboard,
+ private void setPrimaryClipInternalNoClassifyLocked(Clipboard clipboard,
@Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
revokeUris(clipboard);
clipboard.activePermissionOwners.clear();
@@ -775,7 +918,7 @@
sendClipChangedBroadcast(clipboard);
}
- private void sendClipChangedBroadcast(PerUserClipboard clipboard) {
+ private void sendClipChangedBroadcast(Clipboard clipboard) {
final long ident = Binder.clearCallingIdentity();
final int n = clipboard.primaryClipListeners.beginBroadcast();
try {
@@ -789,7 +932,8 @@
li.mPackageName,
li.mAttributionTag,
li.mUid,
- UserHandle.getUserId(li.mUid))) {
+ UserHandle.getUserId(li.mUid),
+ clipboard.deviceId)) {
clipboard.primaryClipListeners.getBroadcastItem(i)
.dispatchPrimaryClipChanged();
}
@@ -805,7 +949,8 @@
}
@GuardedBy("mLock")
- private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId) {
+ private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId,
+ int deviceId) {
CharSequence text = (clip.getItemCount() == 0) ? null : clip.getItemAt(0).getText();
if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength) {
clip.getDescription().setClassificationStatus(
@@ -830,12 +975,13 @@
ClipDescription.CLASSIFICATION_NOT_PERFORMED);
return;
}
- mWorkerHandler.post(() -> doClassification(text, clip, classifier, userId));
+ mWorkerHandler.post(() -> doClassification(text, clip, classifier, userId, deviceId));
}
@WorkerThread
private void doClassification(
- CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId) {
+ CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId,
+ int deviceId) {
TextLinks.Request request = new TextLinks.Request.Builder(text).build();
TextLinks links = classifier.generateLinks(request);
@@ -852,7 +998,7 @@
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(userId);
+ Clipboard clipboard = getClipboardLocked(userId, deviceId);
if (clipboard.primaryClip == clip) {
applyClassificationAndSendBroadcastLocked(
clipboard, confidences, links, classifier);
@@ -867,7 +1013,7 @@
final boolean canCopyIntoProfile = !hasRestriction(
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
- PerUserClipboard relatedClipboard = getClipboardLocked(id);
+ Clipboard relatedClipboard = getClipboardLocked(id, deviceId);
if (hasTextLocked(relatedClipboard, text)) {
applyClassificationAndSendBroadcastLocked(
relatedClipboard, confidences, links, classifier);
@@ -882,7 +1028,7 @@
@GuardedBy("mLock")
private void applyClassificationAndSendBroadcastLocked(
- PerUserClipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links,
+ Clipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links,
TextClassifier classifier) {
clipboard.mTextClassifier = classifier;
clipboard.primaryClip.getDescription().setConfidenceScores(confidences);
@@ -893,7 +1039,7 @@
}
@GuardedBy("mLock")
- private boolean hasTextLocked(PerUserClipboard clipboard, @NonNull CharSequence text) {
+ private boolean hasTextLocked(Clipboard clipboard, @NonNull CharSequence text) {
return clipboard.primaryClip != null
&& clipboard.primaryClip.getItemCount() > 0
&& text.equals(clipboard.primaryClip.getItemAt(0).getText());
@@ -972,7 +1118,7 @@
}
@GuardedBy("mLock")
- private void addActiveOwnerLocked(int uid, String pkg) {
+ private void addActiveOwnerLocked(int uid, int deviceId, String pkg) {
final IPackageManager pm = AppGlobals.getPackageManager();
final int targetUserHandle = UserHandle.getCallingUserId();
final long oldIdentity = Binder.clearCallingIdentity();
@@ -990,7 +1136,7 @@
} finally {
Binder.restoreCallingIdentity(oldIdentity);
}
- PerUserClipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid));
+ Clipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid), deviceId);
if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
final int N = clipboard.primaryClip.getItemCount();
for (int i=0; i<N; i++) {
@@ -1025,7 +1171,7 @@
}
}
- private void revokeUris(PerUserClipboard clipboard) {
+ private void revokeUris(Clipboard clipboard) {
if (clipboard.primaryClip == null) {
return;
}
@@ -1036,8 +1182,14 @@
}
private boolean clipboardAccessAllowed(
- int op, String callingPackage, String attributionTag, int uid, @UserIdInt int userId) {
- return clipboardAccessAllowed(op, callingPackage, attributionTag, uid, userId, true);
+ int op,
+ String callingPackage,
+ String attributionTag,
+ int uid,
+ @UserIdInt int userId,
+ int intendingDeviceId) {
+ return clipboardAccessAllowed(op, callingPackage, attributionTag, uid, userId,
+ intendingDeviceId, true);
}
private boolean clipboardAccessAllowed(
@@ -1046,6 +1198,7 @@
String attributionTag,
int uid,
@UserIdInt int userId,
+ int intendingDeviceId,
boolean shouldNoteOp) {
boolean allowed;
@@ -1053,10 +1206,9 @@
// First, verify package ownership to ensure use below is safe.
mAppOps.checkPackage(uid, callingPackage);
- // Nothing in a virtual session is permitted to touch clipboard contents
- if (mVdm != null && mVdm.isAppRunningOnAnyVirtualDevice(uid)) {
+ if (intendingDeviceId == DEVICE_ID_INVALID) {
Slog.w(TAG, "Clipboard access denied to " + uid + "/" + callingPackage
- + " within a virtual device session");
+ + " due to invalid device id");
return false;
}
@@ -1077,7 +1229,8 @@
// Binder.getCallingUid(). Without checking, the user X can't copy any thing from
// INTERNAL_SYSTEM_WINDOW to the other applications.
if (!allowed) {
- allowed = mWm.isUidFocused(uid)
+ allowed = isDefaultDeviceAndUidFocused(intendingDeviceId, uid)
+ || isVirtualDeviceAndUidFocused(intendingDeviceId, uid)
|| isInternalSysWindowAppWithWindowFocus(callingPackage);
}
if (!allowed && mContentCaptureInternal != null) {
@@ -1098,6 +1251,12 @@
// userId must pass intending userId. i.e. user#10.
allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);
}
+ if (!allowed && intendingDeviceId != DEVICE_ID_DEFAULT) {
+ // Privileged apps which own a VirtualDevice are allowed to read its clipboard
+ // in the background.
+ allowed = (mVdmInternal != null)
+ && mVdmInternal.getDeviceOwnerUid(intendingDeviceId) == uid;
+ }
break;
case AppOpsManager.OP_WRITE_CLIPBOARD:
// Writing is allowed without focus.
@@ -1123,6 +1282,19 @@
return appOpsResult == AppOpsManager.MODE_ALLOWED;
}
+ private boolean isDefaultDeviceAndUidFocused(int intendingDeviceId, int uid) {
+ return intendingDeviceId == DEVICE_ID_DEFAULT && mWm.isUidFocused(uid);
+ }
+
+ private boolean isVirtualDeviceAndUidFocused(int intendingDeviceId, int uid) {
+ if (intendingDeviceId == DEVICE_ID_DEFAULT || mVdm == null) {
+ return false;
+ }
+ int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
+ int focusedDeviceId = mVdm.getDeviceIdForDisplayId(topFocusedDisplayId);
+ return (focusedDeviceId == intendingDeviceId) && mWm.isUidFocused(uid);
+ }
+
private boolean isDefaultIme(int userId, String packageName) {
String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, userId);
@@ -1141,7 +1313,7 @@
*/
@GuardedBy("mLock")
private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,
- PerUserClipboard clipboard) {
+ Clipboard clipboard) {
if (clipboard.primaryClip == null) {
return;
}
@@ -1174,8 +1346,8 @@
if (clipboard.mNotifiedUids.get(uid)) {
return;
}
- clipboard.mNotifiedUids.put(uid, true);
+ final ArraySet<Context> toastContexts = getToastContexts(clipboard);
Binder.withCleanCallingIdentity(() -> {
try {
CharSequence callingAppLabel = mPm.getApplicationLabel(
@@ -1183,23 +1355,72 @@
String message =
getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
Slog.i(TAG, message);
- Toast toastToShow;
- if (SafetyProtectionUtils.shouldShowSafetyProtectionResources(getContext())) {
- Drawable safetyProtectionIcon = getContext()
- .getDrawable(R.drawable.ic_safety_protection);
- toastToShow = Toast.makeCustomToastWithIcon(getContext(),
- UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT, safetyProtectionIcon);
- } else {
- toastToShow = Toast.makeText(
- getContext(), UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT);
+ for (int i = 0; i < toastContexts.size(); i++) {
+ Context toastContext = toastContexts.valueAt(i);
+ Toast toastToShow;
+ if (SafetyProtectionUtils.shouldShowSafetyProtectionResources(getContext())) {
+ Drawable safetyProtectionIcon = getContext()
+ .getDrawable(R.drawable.ic_safety_protection);
+ toastToShow = Toast.makeCustomToastWithIcon(toastContext,
+ UiThread.get().getLooper(), message,
+ Toast.LENGTH_SHORT, safetyProtectionIcon);
+ } else {
+ toastToShow = Toast.makeText(
+ toastContext, UiThread.get().getLooper(), message,
+ Toast.LENGTH_SHORT);
+ }
+ toastToShow.show();
}
- toastToShow.show();
} catch (PackageManager.NameNotFoundException e) {
// do nothing
}
});
+
+ clipboard.mNotifiedUids.put(uid, true);
+ }
+
+ /**
+ * Returns the context(s) to use for toasts related to this clipboard. Normally this will just
+ * contain a single context referencing the default display.
+ *
+ * If the clipboard is for a VirtualDevice, we attempt to return the single DisplayContext for
+ * the focused VirtualDisplay for that device, but might need to return the contexts for
+ * multiple displays if the VirtualDevice has several but none of them were focused.
+ */
+ private ArraySet<Context> getToastContexts(Clipboard clipboard) throws IllegalStateException {
+ ArraySet<Context> contexts = new ArraySet<>();
+
+ if (clipboard.deviceId != DEVICE_ID_DEFAULT) {
+ DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
+
+ int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
+ ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(clipboard.deviceId);
+
+ if (displayIds.contains(topFocusedDisplayId)) {
+ Display display = displayManager.getDisplay(topFocusedDisplayId);
+ if (display != null) {
+ contexts.add(getContext().createDisplayContext(display));
+ return contexts;
+ }
+ }
+
+ for (int i = 0; i < displayIds.size(); i++) {
+ Display display = displayManager.getDisplay(displayIds.valueAt(i));
+ if (display != null) {
+ contexts.add(getContext().createDisplayContext(display));
+ }
+ }
+ if (!contexts.isEmpty()) {
+ return contexts;
+ }
+ Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
+ + clipboard.deviceId);
+ // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
+ // the default display below.
+ }
+
+ contexts.add(getContext());
+ return contexts;
}
/**
@@ -1221,7 +1442,7 @@
/** Potentially notifies the text classifier that an app is accessing a text clip. */
@GuardedBy("mLock")
private void notifyTextClassifierLocked(
- PerUserClipboard clipboard, String callingPackage, int callingUid) {
+ Clipboard clipboard, String callingPackage, int callingUid) {
if (clipboard.primaryClip == null) {
return;
}
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 974c04b..001cb10 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.companion.virtual.IVirtualDevice;
import android.os.LocaleList;
+import android.util.ArraySet;
import java.util.Set;
@@ -84,7 +85,7 @@
* *Note* this only checks VirtualDevices, and does not include information about whether
* the app is running on the default device or not.
*/
- public abstract @NonNull Set<Integer> getDeviceIdsForUid(int uid);
+ public abstract @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid);
/**
* Notifies that a virtual display is created.
@@ -132,4 +133,12 @@
* Returns true if the {@code displayId} is owned by any virtual device
*/
public abstract boolean isDisplayOwnedByAnyVirtualDevice(int displayId);
+
+ /**
+ * Gets the ids of VirtualDisplays owned by a VirtualDevice.
+ *
+ * @param deviceId which device we're asking about
+ * @return the set of display ids for all VirtualDisplays owned by the device
+ */
+ public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId);
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 5b696c2..ec2e254 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -206,13 +206,6 @@
*/
private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds
- /**
- * Generate job ids in the range [MIN_SYNC_JOB_ID, MAX_SYNC_JOB_ID) to avoid conflicts with
- * other jobs scheduled by the system process.
- */
- private static final int MIN_SYNC_JOB_ID = 100000;
- private static final int MAX_SYNC_JOB_ID = 110000;
-
private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
@@ -243,12 +236,11 @@
volatile private PowerManager.WakeLock mSyncManagerWakeLock;
volatile private boolean mDataConnectionIsConnected = false;
- private volatile int mNextJobIdOffset = 0;
+ private volatile int mNextJobId = 0;
private final NotificationManager mNotificationMgr;
private final IBatteryStats mBatteryStats;
private JobScheduler mJobScheduler;
- private JobSchedulerInternal mJobSchedulerInternal;
private SyncStorageEngine mSyncStorageEngine;
@@ -284,24 +276,19 @@
}
private int getUnusedJobIdH() {
- final int maxNumSyncJobIds = MAX_SYNC_JOB_ID - MIN_SYNC_JOB_ID;
- final List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
- for (int i = 0; i < maxNumSyncJobIds; ++i) {
- int newJobId = MIN_SYNC_JOB_ID + ((mNextJobIdOffset + i) % maxNumSyncJobIds);
- if (!isJobIdInUseLockedH(newJobId, pendingJobs)) {
- mNextJobIdOffset = (mNextJobIdOffset + i + 1) % maxNumSyncJobIds;
- return newJobId;
- }
+ final List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
+ while (isJobIdInUseLockedH(mNextJobId, pendingJobs)) {
+ // SyncManager jobs are placed in their own namespace. Since there's no chance of
+ // conflicting with other parts of the system, we can just keep incrementing until
+ // we find an unused ID.
+ mNextJobId++;
}
- // We've used all 10,000 intended job IDs.... We're probably in a world of pain right now :/
- Slog.wtf(TAG, "All " + maxNumSyncJobIds + " possible sync job IDs are taken :/");
- mNextJobIdOffset = (mNextJobIdOffset + 1) % maxNumSyncJobIds;
- return MIN_SYNC_JOB_ID + mNextJobIdOffset;
+ return mNextJobId;
}
private List<SyncOperation> getAllPendingSyncs() {
verifyJobScheduler();
- List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
+ List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
final int numJobs = pendingJobs.size();
final List<SyncOperation> pendingSyncs = new ArrayList<>(numJobs);
for (int i = 0; i < numJobs; ++i) {
@@ -309,6 +296,8 @@
SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
if (op != null) {
pendingSyncs.add(op);
+ } else {
+ Slog.wtf(TAG, "Non-sync job inside of SyncManager's namespace");
}
}
return pendingSyncs;
@@ -494,6 +483,38 @@
});
}
+ /**
+ * Migrate syncs from the default job namespace to SyncManager's namespace if they haven't been
+ * migrated already.
+ */
+ private void migrateSyncJobNamespaceIfNeeded() {
+ if (mSyncStorageEngine.isJobNamespaceMigrated()) {
+ return;
+ }
+ final JobScheduler jobSchedulerDefaultNamespace =
+ mContext.getSystemService(JobScheduler.class);
+ final List<JobInfo> pendingJobs = jobSchedulerDefaultNamespace.getAllPendingJobs();
+ // Wait until we've confirmed that all syncs have been migrated to the new namespace
+ // before we persist successful migration to our status file. This is done to avoid
+ // internal consistency issues if the devices reboots right after SyncManager has
+ // done the migration on its side but before JobScheduler has finished persisting
+ // the updated jobs to disk. If JobScheduler hasn't persisted the update to disk,
+ // then nothing that happened afterwards should have been persisted either, so there's
+ // no concern over activity happening after the migration causing issues.
+ boolean allSyncsMigrated = true;
+ for (int i = pendingJobs.size() - 1; i >= 0; --i) {
+ final JobInfo job = pendingJobs.get(i);
+ final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
+ if (op != null) {
+ // This is a sync. Move it over to SyncManager's namespace.
+ mJobScheduler.schedule(job);
+ jobSchedulerDefaultNamespace.cancel(job.getId());
+ allSyncsMigrated = false;
+ }
+ }
+ mSyncStorageEngine.setJobNamespaceMigrated(allSyncsMigrated);
+ }
+
private synchronized void verifyJobScheduler() {
if (mJobScheduler != null) {
return;
@@ -503,10 +524,12 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "initializing JobScheduler object.");
}
- mJobScheduler = (JobScheduler) mContext.getSystemService(
- Context.JOB_SCHEDULER_SERVICE);
- mJobSchedulerInternal = getJobSchedulerInternal();
- // Get all persisted syncs from JobScheduler
+ // Use a dedicated namespace to avoid conflicts with other jobs
+ // scheduled by the system process.
+ mJobScheduler = mContext.getSystemService(JobScheduler.class)
+ .forNamespace("SyncManager");
+ migrateSyncJobNamespaceIfNeeded();
+ // Get all persisted syncs from JobScheduler in the SyncManager namespace.
List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
int numPersistedPeriodicSyncs = 0;
@@ -522,6 +545,8 @@
// shown on the settings activity.
mSyncStorageEngine.markPending(op.target, true);
}
+ } else {
+ Slog.wtf(TAG, "Non-sync job inside of SyncManager namespace");
}
}
final String summary = "Loaded persisted syncs: "
@@ -543,11 +568,6 @@
}
}
- @VisibleForTesting
- protected JobSchedulerInternal getJobSchedulerInternal() {
- return LocalServices.getService(JobSchedulerInternal.class);
- }
-
/**
* @return whether the device most likely has some periodic syncs.
*/
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 9c1cf38..9f3302d 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -172,6 +172,8 @@
private volatile boolean mIsClockValid;
+ private volatile boolean mIsJobNamespaceMigrated;
+
static {
sAuthorityRenames = new HashMap<String, String>();
sAuthorityRenames.put("contacts", "com.android.contacts");
@@ -836,6 +838,20 @@
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target);
}
+ void setJobNamespaceMigrated(boolean migrated) {
+ if (mIsJobNamespaceMigrated == migrated) {
+ return;
+ }
+ mIsJobNamespaceMigrated = migrated;
+ // This isn't urgent enough to write synchronously. Post it to the handler thread so
+ // SyncManager can move on with whatever it was doing.
+ mHandler.sendEmptyMessageDelayed(MSG_WRITE_STATUS, WRITE_STATUS_DELAY);
+ }
+
+ boolean isJobNamespaceMigrated() {
+ return mIsJobNamespaceMigrated;
+ }
+
public Pair<Long, Long> getBackoff(EndPoint info) {
synchronized (mAuthorities) {
AuthorityInfo authority = getAuthorityLocked(info, "getBackoff");
@@ -1585,7 +1601,6 @@
}
}
-
/**
* Remove an authority associated with a provider. Needs to be a standalone function for
* backward compatibility.
@@ -2101,6 +2116,10 @@
mSyncStatus.put(status.authorityId, status);
}
break;
+ case (int) SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED:
+ mIsJobNamespaceMigrated =
+ proto.readBoolean(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
return;
}
@@ -2368,6 +2387,9 @@
}
proto.end(token);
}
+
+ proto.write(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED, mIsJobNamespaceMigrated);
+
proto.flush();
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 4f00835..05464c8 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -196,6 +196,11 @@
// available.
private float mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ // The screen brightness level before clamping and throttling. This value needs to be stored
+ // for concurrent displays mode and passed to the additional displays which will do their own
+ // clamping and throttling.
+ private float mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
// The current display policy. This is useful, for example, for knowing when we're dozing,
// where the light sensor may not be available.
private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF;
@@ -380,6 +385,10 @@
return mScreenAutoBrightness;
}
+ float getRawAutomaticScreenBrightness() {
+ return mRawScreenAutoBrightness;
+ }
+
public boolean hasValidAmbientLux() {
return mAmbientLuxValid;
}
@@ -653,6 +662,7 @@
mPreThresholdLux = PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mRecentLightSamples = 0;
mAmbientLightRingBuffer.clear();
@@ -915,6 +925,7 @@
float value = mCurrentBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
mForegroundAppCategory);
+ mRawScreenAutoBrightness = value;
float newScreenAutoBrightness = clampScreenBrightness(value);
// The min/max range can change for brightness due to HBM. See if the current brightness
@@ -1127,6 +1138,14 @@
}
}
+ public float convertToFloatScale(float nits) {
+ if (mCurrentBrightnessMapper != null) {
+ return mCurrentBrightnessMapper.convertToFloatScale(nits);
+ } else {
+ return -1.0f;
+ }
+ }
+
public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
mCurrentBrightnessMapper.recalculateSplines(applyAdjustment, adjustment);
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 3fc50c4..d7c1529 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -322,6 +322,13 @@
public abstract float convertToNits(float brightness);
/**
+ * Converts the provided nits value to a float value if possible.
+ *
+ * Returns -1.0f if there's no available mapping for the nits to float.
+ */
+ public abstract float convertToFloatScale(float nits);
+
+ /**
* Adds a user interaction data point to the brightness mapping.
*
* This data point <b>must</b> exist on the brightness curve as a result of this call. This is
@@ -671,6 +678,11 @@
}
@Override
+ public float convertToFloatScale(float nits) {
+ return -1.0f;
+ }
+
+ @Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
if (mLoggingEnabled) {
@@ -913,6 +925,11 @@
}
@Override
+ public float convertToFloatScale(float nits) {
+ return mNitsToBrightnessSpline.interpolate(nits);
+ }
+
+ @Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
if (mLoggingEnabled) {
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index 87cbbfe..e27182f 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -28,11 +28,13 @@
private final float mBrightness;
private final float mSdrBrightness;
private final BrightnessReason mBrightnessReason;
+ private final String mDisplayBrightnessStrategyName;
private DisplayBrightnessState(Builder builder) {
this.mBrightness = builder.getBrightness();
this.mSdrBrightness = builder.getSdrBrightness();
this.mBrightnessReason = builder.getBrightnessReason();
+ this.mDisplayBrightnessStrategyName = builder.getDisplayBrightnessStrategyName();
}
/**
@@ -56,6 +58,14 @@
return mBrightnessReason;
}
+ /**
+ * Gets the {@link com.android.server.display.brightness.strategy.DisplayBrightnessStrategy}
+ * name
+ */
+ public String getDisplayBrightnessStrategyName() {
+ return mDisplayBrightnessStrategyName;
+ }
+
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -93,6 +103,10 @@
if (!mBrightnessReason.equals(displayBrightnessState.getBrightnessReason())) {
return false;
}
+ if (!mDisplayBrightnessStrategyName.equals(
+ displayBrightnessState.getDisplayBrightnessStrategyName())) {
+ return false;
+ }
return true;
}
@@ -108,6 +122,7 @@
private float mBrightness;
private float mSdrBrightness;
private BrightnessReason mBrightnessReason = new BrightnessReason();
+ private String mDisplayBrightnessStrategyName;
/**
* Gets the brightness
@@ -164,6 +179,27 @@
}
/**
+ * Gets the {@link com.android.server.display.brightness.strategy.DisplayBrightnessStrategy}
+ * name
+ */
+ public String getDisplayBrightnessStrategyName() {
+ return mDisplayBrightnessStrategyName;
+ }
+
+ /**
+ * Sets the
+ * {@link com.android.server.display.brightness.strategy.DisplayBrightnessStrategy}'s name
+ *
+ * @param displayBrightnessStrategyName The name of the
+ * {@link com.android.server.display.brightness.strategy.DisplayBrightnessStrategy} being
+ * used.
+ */
+ public Builder setDisplayBrightnessStrategyName(String displayBrightnessStrategyName) {
+ this.mDisplayBrightnessStrategyName = displayBrightnessStrategyName;
+ return this;
+ }
+
+ /**
* This is used to construct an immutable DisplayBrightnessState object from its builder
*/
public DisplayBrightnessState build() {
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index 864510e..fa9a100 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.os.IBinder;
+import android.view.Display;
import java.util.Objects;
@@ -32,6 +33,9 @@
private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
private static native long[] nativeGetPhysicalDisplayIds();
private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
+ private static native void nativeSetHdrConversionMode(int conversionMode,
+ int preferredHdrOutputType, int[] autoHdrTypes, int autoHdrTypesLength);
+ private static native int[] nativeGetSupportedHdrOutputTypes();
/**
* Create a display in SurfaceFlinger.
@@ -79,4 +83,20 @@
public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
return nativeGetPhysicalDisplayToken(physicalDisplayId);
}
+
+ /**
+ * @hide
+ */
+ public static void setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
+ int[] autoHdrTypes) {
+ int length = autoHdrTypes != null ? autoHdrTypes.length : 0;
+ nativeSetHdrConversionMode(conversionMode, preferredHdrOutputType, autoHdrTypes, length);
+ }
+
+ /**
+ * @hide
+ */
+ public static @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypes() {
+ return nativeGetSupportedHdrOutputTypes();
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index e557b50..e048a0f 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -17,12 +17,14 @@
package com.android.server.display;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
+import android.hardware.input.HostUsiVersion;
import android.os.Environment;
import android.os.PowerManager;
import android.text.TextUtils;
@@ -57,6 +59,7 @@
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.config.ThermalThrottling;
import com.android.server.display.config.ThresholdPoint;
+import com.android.server.display.config.UsiVersion;
import com.android.server.display.config.XmlParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -132,6 +135,16 @@
* <brightness>0.01</brightness>
* </brightnessThrottlingPoint>
* </brightnessThrottlingMap>
+ * <concurrentDisplaysBrightnessThrottlingMap>
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>severe</thermalStatus>
+ * <brightness>0.07</brightness>
+ * </brightnessThrottlingPoint>
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>critical</thermalStatus>
+ * <brightness>0.005</brightness>
+ * </brightnessThrottlingPoint>
+ * </concurrentDisplaysBrightnessThrottlingMap>
* </thermalThrottling>
*
* <refreshRate>
@@ -380,6 +393,12 @@
* <item>80</item>
* <item>1500</item>
* </screenOffBrightnessSensorValueToLux>
+ * // The version of the Universal Stylus Initiative (USI) protocol supported by this display.
+ * // This should be omitted if the display does not support USI styluses.
+ * <usiVersion>
+ * <majorVersion>2</majorVersion>
+ * <minorVersion>0</minorVersion>
+ * </usiVersion>
* </displayConfiguration>
* }
* </pre>
@@ -613,6 +632,11 @@
// overwritten value.
private BrightnessThrottlingData mBrightnessThrottlingData;
private BrightnessThrottlingData mOriginalBrightnessThrottlingData;
+ // The concurrent displays mode might need a stricter throttling policy
+ private BrightnessThrottlingData mConcurrentDisplaysBrightnessThrottlingData;
+
+ @Nullable
+ private HostUsiVersion mHostUsiVersion;
@VisibleForTesting
DisplayDeviceConfig(Context context) {
@@ -1258,13 +1282,21 @@
}
/**
- * @return brightness throttling data configuration data for the display.
+ * @return brightness throttling configuration data for the display.
*/
public BrightnessThrottlingData getBrightnessThrottlingData() {
return BrightnessThrottlingData.create(mBrightnessThrottlingData);
}
/**
+ * @return brightness throttling configuration data for the display for the concurrent
+ * displays mode.
+ */
+ public BrightnessThrottlingData getConcurrentDisplaysBrightnessThrottlingData() {
+ return BrightnessThrottlingData.create(mConcurrentDisplaysBrightnessThrottlingData);
+ }
+
+ /**
* @return Auto brightness darkening light debounce
*/
public long getAutoBrightnessDarkeningLightDebounce() {
@@ -1350,6 +1382,15 @@
return mScreenOffBrightnessSensorValueToLux;
}
+ /**
+ * @return The USI version supported by this display, or null if USI is not supported.
+ * @see HostUsiVersion
+ */
+ @Nullable
+ public HostUsiVersion getHostUsiVersion() {
+ return mHostUsiVersion;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -1454,6 +1495,8 @@
+ "\n"
+ ", mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
mScreenOffBrightnessSensorValueToLux)
+ + "\n"
+ + ", mUsiVersion= " + mHostUsiVersion
+ "}";
}
@@ -1503,6 +1546,7 @@
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
loadBrightnessThrottlingMap(config);
+ loadConcurrentDisplaysBrightnessThrottlingMap(config);
loadHighBrightnessModeData(config);
loadQuirks(config);
loadBrightnessRamps(config);
@@ -1514,6 +1558,7 @@
loadAutoBrightnessConfigValues(config);
loadRefreshRateSetting(config);
loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
+ loadUsiVersion(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -1714,13 +1759,13 @@
private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
final ThermalThrottling throttlingConfig = config.getThermalThrottling();
if (throttlingConfig == null) {
- Slog.i(TAG, "no thermal throttling config found");
+ Slog.i(TAG, "No thermal throttling config found");
return;
}
final BrightnessThrottlingMap map = throttlingConfig.getBrightnessThrottlingMap();
if (map == null) {
- Slog.i(TAG, "no brightness throttling map found");
+ Slog.i(TAG, "No brightness throttling map found");
return;
}
@@ -1747,6 +1792,43 @@
}
}
+ private void loadConcurrentDisplaysBrightnessThrottlingMap(DisplayConfiguration config) {
+ final ThermalThrottling throttlingConfig = config.getThermalThrottling();
+ if (throttlingConfig == null) {
+ Slog.i(TAG, "No concurrent displays thermal throttling config found");
+ return;
+ }
+
+ final BrightnessThrottlingMap map =
+ throttlingConfig.getConcurrentDisplaysBrightnessThrottlingMap();
+ if (map == null) {
+ Slog.i(TAG, "No concurrent displays brightness throttling map found");
+ return;
+ }
+
+ final List<BrightnessThrottlingPoint> points = map.getBrightnessThrottlingPoint();
+ // At least 1 point is guaranteed by the display device config schema
+ List<BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+ new ArrayList<>(points.size());
+
+ boolean badConfig = false;
+ for (BrightnessThrottlingPoint point : points) {
+ ThermalStatus status = point.getThermalStatus();
+ if (!thermalStatusIsValid(status)) {
+ badConfig = true;
+ break;
+ }
+
+ throttlingLevels.add(new BrightnessThrottlingData.ThrottlingLevel(
+ convertThermalStatus(status), point.getBrightness().floatValue()));
+ }
+
+ if (!badConfig) {
+ mConcurrentDisplaysBrightnessThrottlingData =
+ BrightnessThrottlingData.create(throttlingLevels);
+ }
+ }
+
private void loadRefreshRateSetting(DisplayConfiguration config) {
final RefreshRateConfigs refreshRateConfigs =
(config == null) ? null : config.getRefreshRate();
@@ -2529,7 +2611,8 @@
}
}
- private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
+ @VisibleForTesting
+ static @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
if (value == null) {
return PowerManager.THERMAL_STATUS_NONE;
}
@@ -2630,6 +2713,15 @@
}
}
+ private void loadUsiVersion(DisplayConfiguration config) {
+ final UsiVersion usiVersion = config.getUsiVersion();
+ mHostUsiVersion = usiVersion != null
+ ? new HostUsiVersion(
+ usiVersion.getMajorVersion().intValue(),
+ usiVersion.getMinorVersion().intValue())
+ : null;
+ }
+
/**
* Uniquely identifies a Sensor, with the combination of Type and Name.
*/
@@ -2855,7 +2947,8 @@
return throttlingLevels.hashCode();
}
- private BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
+ @VisibleForTesting
+ BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
throttlingLevels = new ArrayList<>(inLevels.size());
for (ThrottlingLevel level : inLevels) {
throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, level.brightness));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d558e69..3759a8b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -79,12 +79,14 @@
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.display.HdrConversionMode;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.WifiDisplayStatus;
import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.hardware.input.HostUsiVersion;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.net.Uri;
@@ -109,6 +111,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
@@ -232,11 +235,17 @@
private InputManagerInternal mInputManagerInternal;
private IMediaProjectionManager mProjectionService;
private DeviceStateManagerInternal mDeviceStateManager;
+ @GuardedBy("mSyncRoot")
private int[] mUserDisabledHdrTypes = {};
+ private int[] mSupportedHdrOutputType;
+ @GuardedBy("mSyncRoot")
private boolean mAreUserDisabledHdrTypesAllowed = true;
// Display mode chosen by user.
private Display.Mode mUserPreferredMode;
+ // HDR conversion mode chosen by user
+ @GuardedBy("mSyncRoot")
+ private HdrConversionMode mHdrConversionMode;
// The synchronization root for the display manager.
// This lock guards most of the display manager's state.
@@ -260,6 +269,13 @@
final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
mDisplayWindowPolicyControllers = new SparseArray<>();
+ /**
+ * Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
+ * {@link DisplayDevice#mUniqueId}.
+ */
+ public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
+ new ArrayMap<>();
+
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -872,8 +888,8 @@
}
private void clearUserDisabledHdrTypesLocked() {
- mUserDisabledHdrTypes = new int[]{};
synchronized (mSyncRoot) {
+ mUserDisabledHdrTypes = new int[]{};
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.USER_DISABLED_HDR_FORMATS, "");
}
@@ -1312,8 +1328,9 @@
if (projection != null) {
try {
- if (!getProjectionService().isValidMediaProjection(projection)) {
- throw new SecurityException("Invalid media projection");
+ if (!getProjectionService().isCurrentProjection(projection)) {
+ throw new SecurityException("Cannot create VirtualDisplay with "
+ + "non-current MediaProjection");
}
flags = projection.applyVirtualDisplayFlags(flags);
} catch (RemoteException e) {
@@ -1640,7 +1657,19 @@
DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDisplayChanged();
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
+ + display.getDisplayIdLocked());
+ return;
+ }
+
+ // TODO (b/265793751): Set this DPC as a follower of the default DPC if needed,
+ // clear this DPC's followers if it's not a lead display
+
+ final String uniqueId = device.getUniqueId();
+ HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
+ dpc.onDisplayChanged(hbmMetadata);
}
}
@@ -1698,7 +1727,15 @@
final int displayId = display.getDisplayIdLocked();
final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDisplayChanged();
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
+ + display.getDisplayIdLocked());
+ return;
+ }
+ final String uniqueId = device.getUniqueId();
+ HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
+ dpc.onDisplayChanged(hbmMetadata);
}
}
@@ -1879,6 +1916,24 @@
});
}
+ @GuardedBy("mSyncRoot")
+ private int[] getEnabledAutoHdrTypesLocked() {
+ IntArray autoHdrOutputTypesArray = new IntArray();
+ for (int type : getSupportedHdrOutputTypesInternal()) {
+ boolean isDisabled = false;
+ for (int disabledType : mUserDisabledHdrTypes) {
+ if (type == disabledType) {
+ isDisabled = true;
+ break;
+ }
+ }
+ if (!isDisabled) {
+ autoHdrOutputTypesArray.add(type);
+ }
+ }
+ return autoHdrOutputTypesArray.toArray();
+ }
+
Display.Mode getUserPreferredDisplayModeInternal(int displayId) {
synchronized (mSyncRoot) {
if (displayId == Display.INVALID_DISPLAY) {
@@ -1902,6 +1957,38 @@
}
}
+ // TODO (b/264979880) - Add unit test for HDR output control methods.
+ private void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) {
+ int[] autoHdrOutputTypes = null;
+ synchronized (mSyncRoot) {
+ mHdrConversionMode = hdrConversionMode;
+
+ // For auto mode, all supported HDR types are allowed except the ones specifically
+ // disabled by the user.
+ if (hdrConversionMode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM) {
+ autoHdrOutputTypes = getEnabledAutoHdrTypesLocked();
+ }
+ DisplayControl.setHdrConversionMode(hdrConversionMode.getConversionMode(),
+ hdrConversionMode.getPreferredHdrOutputType(), autoHdrOutputTypes);
+ }
+ }
+
+ private HdrConversionMode getHdrConversionModeInternal() {
+ synchronized (mSyncRoot) {
+ if (mHdrConversionMode != null) {
+ return mHdrConversionMode;
+ }
+ }
+ return new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM);
+ }
+
+ private @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypesInternal() {
+ if (mSupportedHdrOutputType == null) {
+ mSupportedHdrOutputType = DisplayControl.getSupportedHdrOutputTypes();
+ }
+ return mSupportedHdrOutputType;
+ }
+
void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
}
@@ -2309,7 +2396,9 @@
}
// fallthrough
default:
- Slog.w(TAG, "Display " + info + " does not support input device matching.");
+ if (DEBUG) {
+ Slog.w(TAG, "Display " + info + " does not support input device matching.");
+ }
}
return Optional.empty();
}
@@ -2651,6 +2740,26 @@
mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
}
+ private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+ + display.getDisplayIdLocked());
+ return null;
+ }
+
+ final String uniqueId = device.getUniqueId();
+
+ if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
+ return mHighBrightnessModeMetadataMap.get(uniqueId);
+ }
+
+ // HBM Time info not present. Create a new one for this physical display.
+ HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
+ mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
+ return hbmInfo;
+ }
+
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
private void addDisplayPowerControllerLocked(LogicalDisplay display) {
if (mPowerHandler == null) {
@@ -2666,17 +2775,23 @@
display, mSyncRoot);
final DisplayPowerControllerInterface displayPowerController;
+ // If display is internal and has a HighBrightnessModeMetadata mapping, use that.
+ // Or create a new one and use that.
+ // We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
+ // displayPowerController, so the hbm info can be correctly associated
+ // with the corresponding displaydevice.
+ HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
if (DeviceConfig.getBoolean("display_manager",
"use_newly_structured_display_power_controller", true)) {
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display));
+ () -> handleBrightnessChange(display), hbmMetadata);
} else {
displayPowerController = new DisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display));
+ () -> handleBrightnessChange(display), hbmMetadata);
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
@@ -3137,7 +3252,9 @@
@Override // Binder call
public int[] getUserDisabledHdrTypes() {
- return mUserDisabledHdrTypes;
+ synchronized (mSyncRoot) {
+ return mUserDisabledHdrTypes;
+ }
}
@Override // Binder call
@@ -3556,6 +3673,40 @@
}
@Override // Binder call
+ public void setHdrConversionMode(HdrConversionMode hdrConversionMode) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MODIFY_HDR_CONVERSION_MODE,
+ "Permission required to set the HDR conversion mode.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setHdrConversionModeInternal(hdrConversionMode);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public HdrConversionMode getHdrConversionMode() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getHdrConversionModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Display.HdrCapabilities.HdrType
+ @Override // Binder call
+ public int[] getSupportedHdrOutputTypes() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getSupportedHdrOutputTypesInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
@@ -4004,6 +4155,19 @@
return SurfaceControl.getDisplayNativePrimaries(displayToken);
}
+
+ @Override
+ public HostUsiVersion getHostUsiVersion(int displayId) {
+ synchronized (mSyncRoot) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (display == null) {
+ return null;
+ }
+
+ return display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()
+ .getHostUsiVersion();
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index c960416..52e20d6 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -635,7 +635,9 @@
// which is within the render rate range
// - 90hz is not in range as none of the even divisors (i.e. 90, 45, 30)
// fall within the acceptable render range.
- final int divisor = (int) Math.ceil(physicalRefreshRate / summary.maxRenderFrameRate);
+ final int divisor =
+ (int) Math.ceil((physicalRefreshRate / summary.maxRenderFrameRate)
+ - FLOAT_TOLERANCE);
float adjustedPhysicalRefreshRate = physicalRefreshRate / divisor;
if (adjustedPhysicalRefreshRate < (summary.minRenderFrameRate - FLOAT_TOLERANCE)) {
if (mLoggingEnabled) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index cdaa3d0..fc2a4e5 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -54,6 +54,7 @@
import android.util.MutableFloat;
import android.util.MutableInt;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.Display;
@@ -388,6 +389,7 @@
private float[] mNitsRange;
private final HighBrightnessModeController mHbmController;
+ private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
private final BrightnessThrottler mBrightnessThrottler;
@@ -449,6 +451,10 @@
// PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary brightness set.
private float mTemporaryScreenBrightness;
+ // This brightness value is set in concurrent displays mode. It is the brightness value
+ // of the lead display that this DPC should follow.
+ private float mBrightnessToFollow;
+
// The last auto brightness adjustment that was set by the user and not temporary. Set to
// Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
private float mAutoBrightnessAdjustment;
@@ -498,6 +504,12 @@
private boolean mIsEnabled;
private boolean mIsInTransition;
+ // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
+ // is one lead display, the additional displays follow the brightness value of the lead display.
+ @GuardedBy("mLock")
+ private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
+ new SparseArray();
+
/**
* Creates the display power controller.
*/
@@ -505,13 +517,14 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable) {
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
mTag = "DisplayPowerController[" + mDisplayId + "]";
+ mHighBrightnessModeMetadata = hbmMetadata;
mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
@@ -633,6 +646,7 @@
loadProximitySensor();
mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
+ mBrightnessToFollow = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
@@ -699,6 +713,48 @@
}
}
+ @Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
+ public void setBrightnessToFollow(float leadDisplayBrightness, float nits) {
+ if (mAutomaticBrightnessController == null || nits < 0) {
+ mBrightnessToFollow = leadDisplayBrightness;
+ } else {
+ float brightness = mAutomaticBrightnessController.convertToFloatScale(nits);
+ if (isValidBrightnessValue(brightness)) {
+ mBrightnessToFollow = brightness;
+ } else {
+ // The device does not support nits
+ mBrightnessToFollow = leadDisplayBrightness;
+ }
+ }
+ sendUpdatePowerState();
+ }
+
+ @Override
+ public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+ synchronized (mLock) {
+ mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower);
+ }
+ sendUpdatePowerState();
+ }
+
+ @Override
+ public void clearDisplayBrightnessFollowers() {
+ SparseArray<DisplayPowerControllerInterface> followers;
+ synchronized (mLock) {
+ followers = mDisplayBrightnessFollowers.clone();
+ mDisplayBrightnessFollowers.clear();
+ }
+ for (int i = 0; i < followers.size(); i++) {
+ DisplayPowerControllerInterface follower = followers.valueAt(i);
+ follower.setBrightnessToFollow(PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1);
+ }
+ }
+
@Nullable
@Override
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -790,7 +846,7 @@
* Make sure DisplayManagerService.mSyncRoot is held when this is called
*/
@Override
- public void onDisplayChanged() {
+ public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
@@ -812,7 +868,7 @@
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token, info);
+ loadFromDisplayDeviceConfig(token, info, hbmMetadata);
/// Since the underlying display-device changed, we really don't know the
// last command that was sent to change it's state. Lets assume it is unknown so
@@ -829,6 +885,9 @@
updatePowerState();
}
});
+
+ // TODO (b/265793751): Re-create BrightnessTracker if we're enetering/exiting concurrent
+ // displays mode
}
/**
@@ -864,7 +923,8 @@
}
}
- private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
+ HighBrightnessModeMetadata hbmMetadata) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadBrightnessRampRates();
@@ -877,6 +937,7 @@
mBrightnessRampIncreaseMaxTimeMillis,
mBrightnessRampDecreaseMaxTimeMillis);
}
+ mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -885,6 +946,8 @@
return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
}
});
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
mBrightnessThrottler.resetThrottlingData(
mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
}
@@ -1102,6 +1165,8 @@
}
loadScreenOffBrightnessSensor();
int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+ // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
+ // a complementary display
if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
mSensorManager,
@@ -1237,6 +1302,7 @@
int brightnessAdjustmentFlags = 0;
mBrightnessReasonTemp.set(null);
mTempBrightnessEvent.reset();
+ SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers;
synchronized (mLock) {
if (mStopped) {
return;
@@ -1265,6 +1331,8 @@
}
mustNotify = !mDisplayReadyLocked;
+
+ displayBrightnessFollowers = mDisplayBrightnessFollowers.clone();
}
// Compute the basic display state using the policy.
@@ -1372,6 +1440,11 @@
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
}
+ if (Float.isNaN(brightnessState) && isValidBrightnessValue(mBrightnessToFollow)) {
+ brightnessState = mBrightnessToFollow;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_FOLLOWER);
+ }
+
if ((Float.isNaN(brightnessState))
&& isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {
brightnessState = mPowerRequest.screenBrightnessOverride;
@@ -1386,7 +1459,8 @@
final boolean autoBrightnessEnabled = mUseAutoBrightness
&& (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& Float.isNaN(brightnessState)
- && mAutomaticBrightnessController != null;
+ && mAutomaticBrightnessController != null
+ && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER;
final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness
&& !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
final int autoBrightnessState = autoBrightnessEnabled
@@ -1455,12 +1529,15 @@
}
boolean updateScreenBrightnessSetting = false;
+ float rawBrightnessState = brightnessState;
// Apply auto-brightness.
boolean slowChange = false;
if (Float.isNaN(brightnessState)) {
float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
if (autoBrightnessEnabled) {
+ rawBrightnessState = mAutomaticBrightnessController
+ .getRawAutomaticScreenBrightness();
brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
mTempBrightnessEvent);
newAutoBrightnessAdjustment =
@@ -1502,7 +1579,8 @@
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
- brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+ rawBrightnessState = mScreenBrightnessDozeConfig;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
}
@@ -1510,7 +1588,9 @@
// brightness
if (Float.isNaN(brightnessState) && autoBrightnessEnabled
&& mScreenOffBrightnessSensorController != null) {
- brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ rawBrightnessState =
+ mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ brightnessState = rawBrightnessState;
if (isValidBrightnessValue(brightnessState)) {
brightnessState = clampScreenBrightness(brightnessState);
updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
@@ -1521,7 +1601,8 @@
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
- brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+ rawBrightnessState = mCurrentScreenBrightnessSetting;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
if (brightnessState != mCurrentScreenBrightnessSetting) {
// The manually chosen screen brightness is outside of the currently allowed
// range (i.e., high-brightness-mode), make sure we tell the rest of the system
@@ -1553,6 +1634,11 @@
mAppliedThrottling = false;
}
+ for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
+ DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
+ follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState));
+ }
+
if (updateScreenBrightnessSetting) {
// Tell the rest of the system about the new brightness in case we had to change it
// for things like auto-brightness or high-brightness-mode. Note that we do this
@@ -1961,12 +2047,14 @@
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
}
- }, mContext);
+ }, mHighBrightnessModeMetadata, mContext);
}
private BrightnessThrottler createBrightnessThrottlerLocked() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
final DisplayDeviceConfig.BrightnessThrottlingData data =
ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
return new BrightnessThrottler(mHandler, data,
@@ -2664,6 +2752,7 @@
pw.println(" mPendingScreenBrightnessSetting="
+ mPendingScreenBrightnessSetting);
pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
+ pw.println(" mBrightnessToFollow=" + mBrightnessToFollow);
pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
pw.println(" mBrightnessReason=" + mBrightnessReason);
pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
@@ -2796,6 +2885,9 @@
}
private void noteScreenState(int screenState) {
+ // Log screen state change with display id
+ FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED_V2,
+ screenState, mDisplayStatsId);
if (mBatteryStats != null) {
try {
// TODO(multi-display): make this multi-display
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index df0e2d3..a744415 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -52,6 +52,7 @@
import android.util.MutableFloat;
import android.util.MutableInt;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import com.android.internal.R;
@@ -327,6 +328,7 @@
private float[] mNitsRange;
private final HighBrightnessModeController mHbmController;
+ private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
private final BrightnessThrottler mBrightnessThrottler;
@@ -408,6 +410,13 @@
private boolean mIsEnabled;
private boolean mIsInTransition;
+
+ // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
+ // is one lead display, the additional displays follow the brightness value of the lead display.
+ @GuardedBy("mLock")
+ private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
+ new SparseArray();
+
/**
* Creates the display power controller.
*/
@@ -415,7 +424,7 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable) {
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -431,6 +440,7 @@
mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
() -> updatePowerState(), mDisplayId, mSensorManager);
+ mHighBrightnessModeMetadata = hbmMetadata;
mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
mTag = "DisplayPowerController2[" + mDisplayId + "]";
@@ -681,7 +691,7 @@
* Make sure DisplayManagerService.mSyncRoot lock is held when this is called
*/
@Override
- public void onDisplayChanged() {
+ public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
@@ -695,6 +705,7 @@
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
+
mHandler.post(() -> {
boolean changed = false;
if (mDisplayDevice != device) {
@@ -703,7 +714,7 @@
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token, info);
+ loadFromDisplayDeviceConfig(token, info, hbmMetadata);
mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
// Since the underlying display-device changed, we really don't know the
@@ -721,6 +732,9 @@
updatePowerState();
}
});
+
+ // TODO (b/265793751): Re-create BrightnessTracker if we're enetering/exiting concurrent
+ // displays mode
}
/**
@@ -750,7 +764,8 @@
}
}
- private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
+ HighBrightnessModeMetadata hbmMetadata) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadBrightnessRampRates();
@@ -762,6 +777,7 @@
mBrightnessRampIncreaseMaxTimeMillis,
mBrightnessRampDecreaseMaxTimeMillis);
}
+ mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -770,6 +786,8 @@
return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
}
});
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
mBrightnessThrottler.resetThrottlingData(
mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
}
@@ -986,6 +1004,8 @@
loadScreenOffBrightnessSensor();
int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+ // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
+ // a complementary display
if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
mSensorManager,
@@ -1105,6 +1125,7 @@
boolean mustInitialize = false;
int brightnessAdjustmentFlags = 0;
mTempBrightnessEvent.reset();
+ SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers;
synchronized (mLock) {
if (mStopped) {
return;
@@ -1133,6 +1154,8 @@
}
mustNotify = !mDisplayReadyLocked;
+
+ displayBrightnessFollowers = mDisplayBrightnessFollowers.clone();
}
int state = mDisplayStateController
@@ -1159,6 +1182,7 @@
DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
.updateBrightness(mPowerRequest, state);
float brightnessState = displayBrightnessState.getBrightness();
+ float rawBrightnessState = displayBrightnessState.getBrightness();
mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
final boolean autoBrightnessEnabledInDoze =
@@ -1169,7 +1193,8 @@
&& (Float.isNaN(brightnessState)
|| mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY
|| mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_BOOST)
- && mAutomaticBrightnessController != null;
+ && mAutomaticBrightnessController != null
+ && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER;
final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness
&& !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
final int autoBrightnessState = autoBrightnessEnabled
@@ -1223,6 +1248,8 @@
if (Float.isNaN(brightnessState)) {
float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
if (autoBrightnessEnabled) {
+ rawBrightnessState = mAutomaticBrightnessController
+ .getRawAutomaticScreenBrightness();
brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
mTempBrightnessEvent);
newAutoBrightnessAdjustment =
@@ -1264,7 +1291,8 @@
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
- brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+ rawBrightnessState = mScreenBrightnessDozeConfig;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
}
@@ -1272,7 +1300,9 @@
// brightness
if (Float.isNaN(brightnessState) && autoBrightnessEnabled
&& mScreenOffBrightnessSensorController != null) {
- brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ rawBrightnessState =
+ mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ brightnessState = rawBrightnessState;
if (BrightnessUtils.isValidBrightnessValue(brightnessState)) {
brightnessState = clampScreenBrightness(brightnessState);
updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness()
@@ -1284,7 +1314,8 @@
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
- brightnessState = clampScreenBrightness(currentBrightnessSetting);
+ rawBrightnessState = currentBrightnessSetting;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
if (brightnessState != currentBrightnessSetting) {
// The manually chosen screen brightness is outside of the currently allowed
// range (i.e., high-brightness-mode), make sure we tell the rest of the system
@@ -1316,6 +1347,11 @@
mAppliedThrottling = false;
}
+ for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
+ DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
+ follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState));
+ }
+
if (updateScreenBrightnessSetting) {
// Tell the rest of the system about the new brightness in case we had to change it
// for things like auto-brightness or high-brightness-mode. Note that we do this
@@ -1512,6 +1548,8 @@
? mCdsi.getReduceBrightColorsStrength() : -1);
mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
mTempBrightnessEvent.setWasShortTermModelActive(hadUserBrightnessPoint);
+ mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState
+ .getDisplayBrightnessStrategyName());
// Temporary is what we use during slider interactions. We avoid logging those so that
// we don't spam logcat when the slider is being used.
boolean tempToTempTransition =
@@ -1718,12 +1756,14 @@
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
}
- }, mContext);
+ }, mHighBrightnessModeMetadata, mContext);
}
private BrightnessThrottler createBrightnessThrottlerLocked() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
final DisplayDeviceConfig.BrightnessThrottlingData data =
ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
return new BrightnessThrottler(mHandler, data,
@@ -2092,6 +2132,27 @@
mDisplayBrightnessController.setBrightness(brightnessValue);
}
+ @Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
+ public void setBrightnessToFollow(float leadDisplayBrightness, float nits) {
+ if (mAutomaticBrightnessController == null || nits < 0) {
+ mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness);
+ } else {
+ float brightness = mAutomaticBrightnessController.convertToFloatScale(nits);
+ if (BrightnessUtils.isValidBrightnessValue(brightness)) {
+ mDisplayBrightnessController.setBrightnessToFollow(brightness);
+ } else {
+ // The device does not support nits
+ mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness);
+ }
+ }
+ sendUpdatePowerState();
+ }
+
private void putAutoBrightnessAdjustmentSetting(float adjustment) {
if (mDisplayId == Display.DEFAULT_DISPLAY) {
mAutoBrightnessAdjustment = adjustment;
@@ -2142,6 +2203,27 @@
}
@Override
+ public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+ synchronized (mLock) {
+ mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower);
+ }
+ sendUpdatePowerState();
+ }
+
+ @Override
+ public void clearDisplayBrightnessFollowers() {
+ SparseArray<DisplayPowerControllerInterface> followers;
+ synchronized (mLock) {
+ followers = mDisplayBrightnessFollowers.clone();
+ mDisplayBrightnessFollowers.clear();
+ }
+ for (int i = 0; i < followers.size(); i++) {
+ DisplayPowerControllerInterface follower = followers.valueAt(i);
+ follower.setBrightnessToFollow(PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1);
+ }
+ }
+
+ @Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
pw.println();
@@ -2310,6 +2392,9 @@
}
private void noteScreenState(int screenState) {
+ // Log screen state change with display id
+ FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED_V2,
+ screenState, mDisplayStatsId);
if (mBatteryStats != null) {
try {
// TODO(multi-display): make this multi-display
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index e750ee2..a95ac74 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -31,10 +31,14 @@
public interface DisplayPowerControllerInterface {
/**
- * Notified when the display is changed. We use this to apply any changes that might be needed
+ * Notified when the display is changed.
+ * We use this to apply any changes that might be needed
* when displays get swapped on foldable devices.
+ * We also pass the High brightness mode metadata like
+ * remaining time and hbm events for the corresponding
+ * physical display, to update the values correctly.
*/
- void onDisplayChanged();
+ void onDisplayChanged(HighBrightnessModeMetadata hbmInfo);
/**
* Unregisters all listeners and interrupts all running threads; halting future work.
@@ -157,4 +161,31 @@
* @param newUserId The new userId
*/
void onSwitchUser(int newUserId);
+
+ /**
+ * Get the ID of the display associated with this DPC.
+ * @return The display ID
+ */
+ int getDisplayId();
+
+ /**
+ * Set the brightness to follow if this is an additional display in a set of concurrent
+ * displays.
+ * @param leadDisplayBrightness The brightness of the lead display in the set of concurrent
+ * displays
+ * @param nits The brightness value in nits if the device supports nits
+ */
+ void setBrightnessToFollow(float leadDisplayBrightness, float nits);
+
+ /**
+ * Add an additional display that will copy the brightness value from this display. This is used
+ * when the device is in concurrent displays mode.
+ * @param follower The DPC that should copy the brightness value from this DPC
+ */
+ void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
+
+ /**
+ * Clear all the additional displays following the brightness value of this display.
+ */
+ void clearDisplayBrightnessFollowers();
}
diff --git a/services/core/java/com/android/server/display/HbmEvent.java b/services/core/java/com/android/server/display/HbmEvent.java
new file mode 100644
index 0000000..5675e2f
--- /dev/null
+++ b/services/core/java/com/android/server/display/HbmEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+
+/**
+ * Represents an event in which High Brightness Mode was enabled.
+ */
+class HbmEvent {
+ private long mStartTimeMillis;
+ private long mEndTimeMillis;
+
+ HbmEvent(long startTimeMillis, long endTimeMillis) {
+ this.mStartTimeMillis = startTimeMillis;
+ this.mEndTimeMillis = endTimeMillis;
+ }
+
+ public long getStartTimeMillis() {
+ return mStartTimeMillis;
+ }
+
+ public long getEndTimeMillis() {
+ return mEndTimeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "HbmEvent: {startTimeMillis:" + mStartTimeMillis + ", endTimeMillis: "
+ + mEndTimeMillis + "}, total: "
+ + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
+ }
+}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index f98c7df..2c843a4 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -105,30 +105,23 @@
private int mHbmStatsState = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
/**
- * If HBM is currently running, this is the start time for the current HBM session.
+ * If HBM is currently running, this is the start time and set of all events,
+ * for the current HBM session.
*/
- private long mRunningStartTimeMillis = -1;
-
- /**
- * Queue of previous HBM-events ordered from most recent to least recent.
- * Meant to store only the events that fall into the most recent
- * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
- */
- private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
-
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadata = null;
HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
- Runnable hbmChangeCallback, Context context) {
+ Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
- brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context);
+ brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, hbmMetadata, context);
}
@VisibleForTesting
HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
- Runnable hbmChangeCallback, Context context) {
+ Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
mInjector = injector;
mContext = context;
mClock = injector.getClock();
@@ -137,6 +130,7 @@
mBrightnessMin = brightnessMin;
mBrightnessMax = brightnessMax;
mHbmChangeCallback = hbmChangeCallback;
+ mHighBrightnessModeMetadata = hbmMetadata;
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mRecalcRunnable = this::recalculateTimeAllowance;
@@ -222,19 +216,22 @@
// If we are starting or ending a high brightness mode session, store the current
// session in mRunningStartTimeMillis, or the old one in mEvents.
- final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1;
+ final long runningStartTime = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+ final boolean wasHbmDrainingAvailableTime = runningStartTime != -1;
final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
&& !mIsHdrLayerPresent;
if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
final long currentTime = mClock.uptimeMillis();
if (shouldHbmDrainAvailableTime) {
- mRunningStartTimeMillis = currentTime;
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
} else {
- mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
- mRunningStartTimeMillis = -1;
+ final HbmEvent hbmEvent = new HbmEvent(runningStartTime, currentTime);
+ mHighBrightnessModeMetadata.addHbmEvent(hbmEvent);
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(-1);
if (DEBUG) {
- Slog.d(TAG, "New HBM event: " + mEvents.peekFirst());
+ Slog.d(TAG, "New HBM event: "
+ + mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst());
}
}
}
@@ -260,6 +257,10 @@
mSettingsObserver.stopObserving();
}
+ void setHighBrightnessModeMetadata(HighBrightnessModeMetadata hbmInfo) {
+ mHighBrightnessModeMetadata = hbmInfo;
+ }
+
void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) {
mWidth = width;
@@ -316,20 +317,22 @@
pw.println(" mBrightnessMax=" + mBrightnessMax);
pw.println(" remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
pw.println(" mIsTimeAvailable= " + mIsTimeAvailable);
- pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
+ pw.println(" mRunningStartTimeMillis="
+ + TimeUtils.formatUptime(mHighBrightnessModeMetadata.getRunningStartTimeMillis()));
pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
pw.println(" width*height=" + mWidth + "*" + mHeight);
pw.println(" mEvents=");
final long currentTime = mClock.uptimeMillis();
long lastStartTime = currentTime;
- if (mRunningStartTimeMillis != -1) {
- lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime));
+ long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+ if (runningStartTimeMillis != -1) {
+ lastStartTime = dumpHbmEvent(pw, new HbmEvent(runningStartTimeMillis, currentTime));
}
- for (HbmEvent event : mEvents) {
- if (lastStartTime > event.endTimeMillis) {
+ for (HbmEvent event : mHighBrightnessModeMetadata.getHbmEventQueue()) {
+ if (lastStartTime > event.getEndTimeMillis()) {
pw.println(" event: [normal brightness]: "
- + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis));
+ + TimeUtils.formatDuration(lastStartTime - event.getEndTimeMillis()));
}
lastStartTime = dumpHbmEvent(pw, event);
}
@@ -338,12 +341,12 @@
}
private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
- final long duration = event.endTimeMillis - event.startTimeMillis;
+ final long duration = event.getEndTimeMillis() - event.getStartTimeMillis();
pw.println(" event: ["
- + TimeUtils.formatUptime(event.startTimeMillis) + ", "
- + TimeUtils.formatUptime(event.endTimeMillis) + "] ("
+ + TimeUtils.formatUptime(event.getStartTimeMillis()) + ", "
+ + TimeUtils.formatUptime(event.getEndTimeMillis()) + "] ("
+ TimeUtils.formatDuration(duration) + ")");
- return event.startTimeMillis;
+ return event.getStartTimeMillis();
}
private boolean isCurrentlyAllowed() {
@@ -372,13 +375,15 @@
// First, lets see how much time we've taken for any currently running
// session of HBM.
- if (mRunningStartTimeMillis > 0) {
- if (mRunningStartTimeMillis > currentTime) {
+ long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+ if (runningStartTimeMillis > 0) {
+ if (runningStartTimeMillis > currentTime) {
Slog.e(TAG, "Start time set to the future. curr: " + currentTime
- + ", start: " + mRunningStartTimeMillis);
- mRunningStartTimeMillis = currentTime;
+ + ", start: " + runningStartTimeMillis);
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
+ runningStartTimeMillis = currentTime;
}
- timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
+ timeAlreadyUsed = currentTime - runningStartTimeMillis;
}
if (DEBUG) {
@@ -387,18 +392,19 @@
// Next, lets iterate through the history of previous sessions and add those times.
final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
- Iterator<HbmEvent> it = mEvents.iterator();
+ Iterator<HbmEvent> it = mHighBrightnessModeMetadata.getHbmEventQueue().iterator();
while (it.hasNext()) {
final HbmEvent event = it.next();
// If this event ended before the current Timing window, discard forever and ever.
- if (event.endTimeMillis < windowstartTimeMillis) {
+ if (event.getEndTimeMillis() < windowstartTimeMillis) {
it.remove();
continue;
}
- final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis);
- timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
+ final long startTimeMillis = Math.max(event.getStartTimeMillis(),
+ windowstartTimeMillis);
+ timeAlreadyUsed += event.getEndTimeMillis() - startTimeMillis;
}
if (DEBUG) {
@@ -425,17 +431,18 @@
// Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
// brightness change doesn't happen before then.
long nextTimeout = -1;
+ final ArrayDeque<HbmEvent> hbmEvents = mHighBrightnessModeMetadata.getHbmEventQueue();
if (mBrightness > mHbmData.transitionPoint) {
// if we're in high-lux now, timeout when we run out of allowed time.
nextTimeout = currentTime + remainingTime;
- } else if (!mIsTimeAvailable && mEvents.size() > 0) {
+ } else if (!mIsTimeAvailable && hbmEvents.size() > 0) {
// If we are not allowed...timeout when the oldest event moved outside of the timing
// window by at least minTime. Basically, we're calculating the soonest time we can
// get {@code timeMinMillis} back to us.
final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
- final HbmEvent lastEvent = mEvents.peekLast();
+ final HbmEvent lastEvent = hbmEvents.peekLast();
final long startTimePlusMinMillis =
- Math.max(windowstartTimeMillis, lastEvent.startTimeMillis)
+ Math.max(windowstartTimeMillis, lastEvent.getStartTimeMillis())
+ mHbmData.timeMinMillis;
final long timeWhenMinIsGainedBack =
currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime;
@@ -459,9 +466,10 @@
+ ", mUnthrottledBrightness: " + mUnthrottledBrightness
+ ", mThrottlingReason: "
+ BrightnessInfo.briMaxReasonToString(mThrottlingReason)
- + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+ + ", RunningStartTimeMillis: "
+ + mHighBrightnessModeMetadata.getRunningStartTimeMillis()
+ ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
- + ", events: " + mEvents);
+ + ", events: " + hbmEvents);
}
if (nextTimeout != -1) {
@@ -588,25 +596,6 @@
}
}
- /**
- * Represents an event in which High Brightness Mode was enabled.
- */
- private static class HbmEvent {
- public long startTimeMillis;
- public long endTimeMillis;
-
- HbmEvent(long startTimeMillis, long endTimeMillis) {
- this.startTimeMillis = startTimeMillis;
- this.endTimeMillis = endTimeMillis;
- }
-
- @Override
- public String toString() {
- return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: "
- + ((endTimeMillis - startTimeMillis) / 1000) + "]";
- }
- }
-
@VisibleForTesting
class HdrListener extends SurfaceControlHdrLayerInfoListener {
@Override
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
new file mode 100644
index 0000000..37234ff
--- /dev/null
+++ b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import java.util.ArrayDeque;
+
+
+/**
+ * Represents High Brightness Mode metadata associated
+ * with a specific internal physical display.
+ * Required for separately storing data like time information,
+ * and related events when display was in HBM mode per
+ * physical internal display.
+ */
+class HighBrightnessModeMetadata {
+ /**
+ * Queue of previous HBM-events ordered from most recent to least recent.
+ * Meant to store only the events that fall into the most recent
+ * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
+ */
+ private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
+
+ /**
+ * If HBM is currently running, this is the start time for the current HBM session.
+ */
+ private long mRunningStartTimeMillis = -1;
+
+ public long getRunningStartTimeMillis() {
+ return mRunningStartTimeMillis;
+ }
+
+ public void setRunningStartTimeMillis(long setTime) {
+ mRunningStartTimeMillis = setTime;
+ }
+
+ public ArrayDeque<HbmEvent> getHbmEventQueue() {
+ return mEvents;
+ }
+
+ public void addHbmEvent(HbmEvent hbmEvent) {
+ mEvents.addFirst(hbmEvent);
+ }
+}
+
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index f19852b..8b09571 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -53,6 +53,7 @@
private int mFlags;
private int mAdjustmentFlags;
private boolean mAutomaticBrightnessEnabled;
+ private String mDisplayBrightnessStrategyName;
public BrightnessEvent(BrightnessEvent that) {
copyFrom(that);
@@ -92,6 +93,7 @@
mAdjustmentFlags = that.getAdjustmentFlags();
// Auto-brightness setting
mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled();
+ mDisplayBrightnessStrategyName = that.getDisplayBrightnessStrategyName();
}
/**
@@ -120,6 +122,7 @@
mAdjustmentFlags = 0;
// Auto-brightness setting
mAutomaticBrightnessEnabled = true;
+ mDisplayBrightnessStrategyName = "";
}
/**
@@ -157,7 +160,8 @@
&& mWasShortTermModelActive == that.mWasShortTermModelActive
&& mFlags == that.mFlags
&& mAdjustmentFlags == that.mAdjustmentFlags
- && mAutomaticBrightnessEnabled == that.mAutomaticBrightnessEnabled;
+ && mAutomaticBrightnessEnabled == that.mAutomaticBrightnessEnabled
+ && mDisplayBrightnessStrategyName.equals(that.mDisplayBrightnessStrategyName);
}
/**
@@ -185,7 +189,8 @@
+ ", wasShortTermModelActive=" + mWasShortTermModelActive
+ ", flags=" + flagsToString()
+ ", reason=" + mReason.toString(mAdjustmentFlags)
- + ", autoBrightness=" + mAutomaticBrightnessEnabled;
+ + ", autoBrightness=" + mAutomaticBrightnessEnabled
+ + ", strategy=" + mDisplayBrightnessStrategyName;
}
@Override
@@ -355,6 +360,14 @@
return mAutomaticBrightnessEnabled;
}
+ public void setDisplayBrightnessStrategyName(String displayBrightnessStrategyName) {
+ mDisplayBrightnessStrategyName = displayBrightnessStrategyName;
+ }
+
+ public String getDisplayBrightnessStrategyName() {
+ return mDisplayBrightnessStrategyName;
+ }
+
public void setAutomaticBrightnessEnabled(boolean mAutomaticBrightnessEnabled) {
this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled;
}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index a952004..d7ae269 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -38,7 +38,8 @@
public static final int REASON_TEMPORARY = 7;
public static final int REASON_BOOST = 8;
public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9;
- public static final int REASON_MAX = REASON_SCREEN_OFF_BRIGHTNESS_SENSOR;
+ public static final int REASON_FOLLOWER = 10;
+ public static final int REASON_MAX = REASON_FOLLOWER;
public static final int MODIFIER_DIMMED = 0x1;
public static final int MODIFIER_LOW_POWER = 0x2;
@@ -193,6 +194,8 @@
return "boost";
case REASON_SCREEN_OFF_BRIGHTNESS_SENSOR:
return "screen_off_brightness_sensor";
+ case REASON_FOLLOWER:
+ return "follower";
default:
return Integer.toString(reason);
}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
index d5aeba1..169cc4a 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
@@ -45,13 +45,15 @@
* A utility to construct the DisplayBrightnessState
*/
public static DisplayBrightnessState constructDisplayBrightnessState(
- int brightnessChangeReason, float brightness, float sdrBrightness) {
+ int brightnessChangeReason, float brightness, float sdrBrightness,
+ String displayBrightnessStrategyName) {
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(brightnessChangeReason);
return new DisplayBrightnessState.Builder()
.setBrightness(brightness)
.setSdrBrightness(sdrBrightness)
.setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(displayBrightnessStrategyName)
.build();
}
}
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index e003ecb..13fcff3 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -129,6 +129,16 @@
}
/**
+ * Sets the brightness to follow
+ */
+ public void setBrightnessToFollow(Float brightnessToFollow) {
+ synchronized (mLock) {
+ mDisplayBrightnessStrategySelector.getFollowerDisplayBrightnessStrategy()
+ .setBrightnessToFollow(brightnessToFollow);
+ }
+ }
+
+ /**
* Returns a boolean flag indicating if the light sensor is to be used to decide the screen
* brightness when dozing
*/
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 7d05f13..02ca2d3 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -28,6 +28,7 @@
import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy;
import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy;
@@ -55,6 +56,8 @@
private final TemporaryBrightnessStrategy mTemporaryBrightnessStrategy;
// The brightness strategy used to manage the brightness state when boost is requested
private final BoostBrightnessStrategy mBoostBrightnessStrategy;
+ // The brightness strategy used for additional displays
+ private final FollowerBrightnessStrategy mFollowerBrightnessStrategy;
// The brightness strategy used to manage the brightness state when the request is invalid.
private final InvalidBrightnessStrategy mInvalidBrightnessStrategy;
@@ -76,6 +79,7 @@
mOverrideBrightnessStrategy = injector.getOverrideBrightnessStrategy();
mTemporaryBrightnessStrategy = injector.getTemporaryBrightnessStrategy();
mBoostBrightnessStrategy = injector.getBoostBrightnessStrategy();
+ mFollowerBrightnessStrategy = injector.getFollowerBrightnessStrategy(displayId);
mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy();
mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean(
R.bool.config_allowAutoBrightnessWhileDozing);
@@ -93,10 +97,13 @@
DisplayBrightnessStrategy displayBrightnessStrategy = mInvalidBrightnessStrategy;
if (targetDisplayState == Display.STATE_OFF) {
displayBrightnessStrategy = mScreenOffBrightnessStrategy;
- } else if (displayPowerRequest.boostScreenBrightness) {
- displayBrightnessStrategy = mBoostBrightnessStrategy;
} else if (shouldUseDozeBrightnessStrategy(displayPowerRequest)) {
displayBrightnessStrategy = mDozeBrightnessStrategy;
+ } else if (BrightnessUtils.isValidBrightnessValue(
+ mFollowerBrightnessStrategy.getBrightnessToFollow())) {
+ displayBrightnessStrategy = mFollowerBrightnessStrategy;
+ } else if (displayPowerRequest.boostScreenBrightness) {
+ displayBrightnessStrategy = mBoostBrightnessStrategy;
} else if (BrightnessUtils
.isValidBrightnessValue(displayPowerRequest.screenBrightnessOverride)) {
displayBrightnessStrategy = mOverrideBrightnessStrategy;
@@ -119,6 +126,10 @@
return mTemporaryBrightnessStrategy;
}
+ public FollowerBrightnessStrategy getFollowerDisplayBrightnessStrategy() {
+ return mFollowerBrightnessStrategy;
+ }
+
/**
* Returns a boolean flag indicating if the light sensor is to be used to decide the screen
* brightness when dozing
@@ -180,6 +191,10 @@
return new BoostBrightnessStrategy();
}
+ FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) {
+ return new FollowerBrightnessStrategy(displayId);
+ }
+
InvalidBrightnessStrategy getInvalidBrightnessStrategy() {
return new InvalidBrightnessStrategy();
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java
index 475ef50..dd400d9 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java
@@ -35,11 +35,12 @@
@Override
public DisplayBrightnessState updateBrightness(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
- // Todo(brup): Introduce a validator class and add validations before setting the brightness
+ // Todo(b/241308599): Introduce a validator class and add validations before setting
+ // the brightness
DisplayBrightnessState displayBrightnessState =
BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_BOOST,
PowerManager.BRIGHTNESS_MAX,
- PowerManager.BRIGHTNESS_MAX);
+ PowerManager.BRIGHTNESS_MAX, getName());
return displayBrightnessState;
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java
index 0bc900b..8299586 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java
@@ -30,9 +30,11 @@
@Override
public DisplayBrightnessState updateBrightness(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
- // Todo(brup): Introduce a validator class and add validations before setting the brightness
+ // Todo(b/241308599): Introduce a validator class and add validations before setting
+ // the brightness
return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_DOZE,
- displayPowerRequest.dozeScreenBrightness, displayPowerRequest.dozeScreenBrightness);
+ displayPowerRequest.dozeScreenBrightness, displayPowerRequest.dozeScreenBrightness,
+ getName());
}
@Override
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
new file mode 100644
index 0000000..090ec13
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.BrightnessUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the brightness of an additional display that copies the brightness value from the lead
+ * display when the device is using concurrent displays.
+ */
+public class FollowerBrightnessStrategy implements DisplayBrightnessStrategy {
+
+ // The ID of the LogicalDisplay using this strategy.
+ private final int mDisplayId;
+
+ // Set to PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no brightness to follow set.
+ private float mBrightnessToFollow;
+
+ public FollowerBrightnessStrategy(int displayId) {
+ mDisplayId = displayId;
+ mBrightnessToFollow = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+
+ @Override
+ public DisplayBrightnessState updateBrightness(
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
+ // Todo(b/241308599): Introduce a validator class and add validations before setting
+ // the brightness
+ return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_FOLLOWER,
+ mBrightnessToFollow, mBrightnessToFollow, getName());
+ }
+
+ @Override
+ public String getName() {
+ return "FollowerBrightnessStrategy";
+ }
+
+ public float getBrightnessToFollow() {
+ return mBrightnessToFollow;
+ }
+
+ public void setBrightnessToFollow(float brightnessToFollow) {
+ mBrightnessToFollow = brightnessToFollow;
+ }
+
+ /**
+ * Dumps the state of this class.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println("FollowerBrightnessStrategy:");
+ writer.println(" mDisplayId=" + mDisplayId);
+ writer.println(" mBrightnessToFollow:" + mBrightnessToFollow);
+ }
+}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java
index 612bbe9..bc24196 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java
@@ -31,7 +31,8 @@
public DisplayBrightnessState updateBrightness(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_UNKNOWN,
- PowerManager.BRIGHTNESS_INVALID_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ PowerManager.BRIGHTNESS_INVALID_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ getName());
}
@Override
diff --git a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
index f03f036..13327cb 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
@@ -29,10 +29,11 @@
@Override
public DisplayBrightnessState updateBrightness(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
- // Todo(brup): Introduce a validator class and add validations before setting the brightness
+ // Todo(b/241308599): Introduce a validator class and add validations before setting
+ // the brightness
return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_OVERRIDE,
displayPowerRequest.screenBrightnessOverride,
- displayPowerRequest.screenBrightnessOverride);
+ displayPowerRequest.screenBrightnessOverride, getName());
}
@Override
diff --git a/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java
index 396fa06..3d411d3 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java
@@ -30,10 +30,11 @@
@Override
public DisplayBrightnessState updateBrightness(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
- // Todo(brup): Introduce a validator class and add validations before setting the brightness
+ // Todo(b/241308599): Introduce a validator class and add validations before setting
+ // the brightness
return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_SCREEN_OFF,
PowerManager.BRIGHTNESS_OFF_FLOAT,
- PowerManager.BRIGHTNESS_OFF_FLOAT);
+ PowerManager.BRIGHTNESS_OFF_FLOAT, getName());
}
@Override
diff --git a/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java
index 7d759ca..35f7dd0 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java
@@ -43,11 +43,12 @@
@Override
public DisplayBrightnessState updateBrightness(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
- // Todo(brup): Introduce a validator class and add validations before setting the brightness
+ // Todo(b/241308599): Introduce a validator class and add validations before setting
+ // the brightness
DisplayBrightnessState displayBrightnessState =
BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_TEMPORARY,
mTemporaryScreenBrightness,
- mTemporaryScreenBrightness);
+ mTemporaryScreenBrightness, getName());
return displayBrightnessState;
}
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index e5357f6..d7306b7 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -118,6 +119,8 @@
// are yet to be delivered.
options.setDeliveryGroupMatchingKey(
DREAMING_DELIVERY_GROUP_NAMESPACE, DREAMING_DELIVERY_GROUP_KEY);
+ // This allows the broadcast delivery to be delayed to apps in the Cached state.
+ options.setDeferUntilActive(true);
return options.toBundle();
}
@@ -152,10 +155,20 @@
+ ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze
+ ", userId=" + userId + ", reason='" + reason + "'");
- if (mCurrentDream != null) {
- mPreviousDreams.add(mCurrentDream);
- }
+ final DreamRecord oldDream = mCurrentDream;
mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock);
+ if (oldDream != null) {
+ if (!oldDream.mWakingGently) {
+ // We will stop these previous dreams once the new dream is started.
+ mPreviousDreams.add(oldDream);
+ } else if (Objects.equals(oldDream.mName, mCurrentDream.mName)) {
+ // We are attempting to start a dream that is currently waking up gently.
+ // Let's silently stop the old instance here to clear the dream state.
+ // This should happen after the new mCurrentDream is set to avoid announcing
+ // a "dream stopped" state.
+ stopDreamInstance(/* immediately */ true, "restarting same dream", oldDream);
+ }
+ }
mCurrentDream.mDreamStartTime = SystemClock.elapsedRealtime();
MetricsLogger.visible(mContext,
@@ -281,7 +294,10 @@
new int[] {ACTIVITY_TYPE_DREAM});
mListener.onDreamStopped(dream.mToken);
+ } else if (dream.mCanDoze && !mCurrentDream.mCanDoze) {
+ mListener.stopDozing(dream.mToken);
}
+
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -327,6 +343,7 @@
*/
public interface Listener {
void onDreamStopped(Binder token);
+ void stopDozing(Binder token);
}
private final class DreamRecord implements DeathRecipient, ServiceConnection {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index a4d2d03..d9cdba7 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -499,7 +499,12 @@
}
synchronized (mLock) {
- if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.isDozing) {
+ if (mCurrentDream == null) {
+ return;
+ }
+
+ final boolean sameDream = mCurrentDream.token == token;
+ if ((sameDream && mCurrentDream.isDozing) || (!sameDream && !mCurrentDream.isDozing)) {
mCurrentDream.isDozing = false;
mDozeWakeLock.release();
mPowerManagerInternal.setDozeOverrideFromDreamManager(
@@ -768,6 +773,11 @@
}
}
}
+
+ @Override
+ public void stopDozing(Binder token) {
+ stopDozingInternal(token);
+ }
};
private final ContentObserver mDozeEnabledObserver = new ContentObserver(null) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index ceb8785..9087354 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -38,6 +38,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.stats.hdmi.HdmiStatsEnums;
import android.util.Slog;
@@ -380,7 +381,7 @@
* Configures the TV panel device wakeup behaviour in standby mode when it receives an OTP
* (One Touch Play) from a source device.
*
- * @param value If true, the TV device will wake up when OTP is received and if false, the TV
+ * @param enabled If true, the TV device will wake up when OTP is received and if false, the TV
* device will not wake up for an OTP.
*/
@ServiceThreadOnly
@@ -393,7 +394,7 @@
/**
* Switch to enable or disable CEC on the device.
*
- * @param value If true, the device will have all CEC functionalities and if false, the device
+ * @param enabled If true, the device will have all CEC functionalities and if false, the device
* will not perform any CEC functions.
*/
@ServiceThreadOnly
@@ -406,8 +407,8 @@
/**
* Configures the module that processes CEC messages - the Android framework or the HAL.
*
- * @param value If true, the Android framework will actively process CEC messages and if false,
- * only the HAL will process the CEC messages.
+ * @param enabled If true, the Android framework will actively process CEC messages.
+ * If false, only the HAL will process the CEC messages.
*/
@ServiceThreadOnly
void enableSystemCecControl(boolean enabled) {
@@ -423,9 +424,8 @@
@ServiceThreadOnly
void setHpdSignalType(@Constants.HpdSignalType int signal, int portId) {
assertRunOnServiceThread();
- // Stub.
- // TODO: bind to native.
- // TODO: handle error return values here, with logging.
+ HdmiLogger.debug("setHpdSignalType: portId %b, signal %b", portId, signal);
+ mNativeWrapperImpl.nativeSetHpdSignalType(signal, portId);
}
/**
@@ -436,9 +436,8 @@
@Constants.HpdSignalType
int getHpdSignalType(int portId) {
assertRunOnServiceThread();
- // Stub.
- // TODO: bind to native.
- return Constants.HDMI_HPD_TYPE_PHYSICAL;
+ HdmiLogger.debug("getHpdSignalType: portId %b ", portId);
+ return mNativeWrapperImpl.nativeGetHpdSignalType(portId);
}
/**
@@ -728,7 +727,7 @@
void sendCommand(final HdmiCecMessage cecMessage,
final HdmiControlService.SendMessageCallback callback) {
assertRunOnServiceThread();
- addCecMessageToHistory(false /* isReceived */, cecMessage);
+ List<String> sendResults = new ArrayList<>();
runOnIoThread(new Runnable() {
@Override
public void run() {
@@ -739,6 +738,12 @@
do {
errorCode = mNativeWrapperImpl.nativeSendCecCommand(
cecMessage.getSource(), cecMessage.getDestination(), body);
+ switch (errorCode) {
+ case SendMessageResult.SUCCESS: sendResults.add("ACK"); break;
+ case SendMessageResult.FAIL: sendResults.add("FAIL"); break;
+ case SendMessageResult.NACK: sendResults.add("NACK"); break;
+ case SendMessageResult.BUSY: sendResults.add("BUSY"); break;
+ }
if (errorCode == SendMessageResult.SUCCESS) {
break;
}
@@ -764,6 +769,8 @@
});
}
});
+
+ addCecMessageToHistory(false /* isReceived */, cecMessage, sendResults);
}
/**
@@ -786,7 +793,7 @@
}
HdmiLogger.debug("[R]:" + command);
- addCecMessageToHistory(true /* isReceived */, command);
+ addCecMessageToHistory(true /* isReceived */, command, null);
mHdmiCecAtomWriter.messageReported(command,
incomingMessageDirection(srcAddress, dstAddress), getCallingUid());
@@ -837,9 +844,10 @@
}
@ServiceThreadOnly
- private void addCecMessageToHistory(boolean isReceived, HdmiCecMessage message) {
+ private void addCecMessageToHistory(boolean isReceived, HdmiCecMessage message,
+ List<String> sendResults) {
assertRunOnServiceThread();
- addEventToHistory(new MessageHistoryRecord(isReceived, message));
+ addEventToHistory(new MessageHistoryRecord(isReceived, message, sendResults));
}
private void addEventToHistory(Dumpable event) {
@@ -906,6 +914,8 @@
void nativeSetLanguage(String language);
void nativeEnableAudioReturnChannel(int port, boolean flag);
boolean nativeIsConnected(int port);
+ void nativeSetHpdSignalType(int signal, int portId);
+ int nativeGetHpdSignalType(int portId);
}
private static final class NativeWrapperImplAidl
@@ -1096,15 +1106,15 @@
HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.length];
int i = 0;
for (android.hardware.tv.hdmi.connection.HdmiPortInfo portInfo : hdmiPortInfos) {
- hdmiPortInfo[i] =
- new HdmiPortInfo(
+ hdmiPortInfo[i] = new HdmiPortInfo.Builder(
portInfo.portId,
portInfo.type,
- portInfo.physicalAddress,
- portInfo.cecSupported,
- false,
- portInfo.arcSupported,
- false);
+ portInfo.physicalAddress)
+ .setCecSupported(portInfo.cecSupported)
+ .setMhlSupported(false)
+ .setArcSupported(portInfo.arcSupported)
+ .setEarcSupported(portInfo.eArcSupported)
+ .build();
i++;
}
return hdmiPortInfo;
@@ -1123,6 +1133,32 @@
return false;
}
}
+
+ @Override
+ public void nativeSetHpdSignalType(int signal, int portId) {
+ try {
+ mHdmiConnection.setHpdSignal((byte) signal, portId);
+ } catch (ServiceSpecificException sse) {
+ HdmiLogger.error(
+ "Could not set HPD signal type for portId " + portId + " to " + signal
+ + ". Error: ", sse.errorCode);
+ } catch (RemoteException e) {
+ HdmiLogger.error(
+ "Could not set HPD signal type for portId " + portId + " to " + signal
+ + ". Exception: ", e);
+ }
+ }
+
+ @Override
+ public int nativeGetHpdSignalType(int portId) {
+ try {
+ return mHdmiConnection.getHpdSignal(portId);
+ } catch (RemoteException e) {
+ HdmiLogger.error(
+ "Could not get HPD signal type for portId " + portId + ". Exception: ", e);
+ return Constants.HDMI_HPD_TYPE_PHYSICAL;
+ }
+ }
}
private static final class NativeWrapperImpl11 implements NativeWrapper,
@@ -1260,13 +1296,15 @@
HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
int i = 0;
for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
- hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
+ hdmiPortInfo[i] = new HdmiPortInfo.Builder(
+ portInfo.portId,
portInfo.type,
- portInfo.physicalAddress,
- portInfo.cecSupported,
- false,
- portInfo.arcSupported,
- false);
+ portInfo.physicalAddress)
+ .setCecSupported(portInfo.cecSupported)
+ .setMhlSupported(false)
+ .setArcSupported(portInfo.arcSupported)
+ .setEarcSupported(false)
+ .build();
i++;
}
return hdmiPortInfo;
@@ -1326,6 +1364,19 @@
return false;
}
}
+
+ @Override
+ public void nativeSetHpdSignalType(int signal, int portId) {
+ HdmiLogger.error(
+ "Failed to set HPD signal type: not supported by HAL.");
+ }
+
+ @Override
+ public int nativeGetHpdSignalType(int portId) {
+ HdmiLogger.error(
+ "Failed to get HPD signal type: not supported by HAL.");
+ return Constants.HDMI_HPD_TYPE_PHYSICAL;
+ }
}
private static final class NativeWrapperImpl implements NativeWrapper,
@@ -1442,13 +1493,15 @@
HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
int i = 0;
for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
- hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
+ hdmiPortInfo[i] = new HdmiPortInfo.Builder(
+ portInfo.portId,
portInfo.type,
- portInfo.physicalAddress,
- portInfo.cecSupported,
- false,
- portInfo.arcSupported,
- false);
+ portInfo.physicalAddress)
+ .setCecSupported(portInfo.cecSupported)
+ .setMhlSupported(false)
+ .setArcSupported(portInfo.arcSupported)
+ .setEarcSupported(false)
+ .build();
i++;
}
return hdmiPortInfo;
@@ -1510,6 +1563,19 @@
}
@Override
+ public void nativeSetHpdSignalType(int signal, int portId) {
+ HdmiLogger.error(
+ "Failed to set HPD signal type: not supported by HAL.");
+ }
+
+ @Override
+ public int nativeGetHpdSignalType(int portId) {
+ HdmiLogger.error(
+ "Failed to get HPD signal type: not supported by HAL.");
+ return Constants.HDMI_HPD_TYPE_PHYSICAL;
+ }
+
+ @Override
public void serviceDied(long cookie) {
if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) {
HdmiLogger.error("Service died cookie : " + cookie + "; reconnecting");
@@ -1661,11 +1727,13 @@
private static final class MessageHistoryRecord extends Dumpable {
private final boolean mIsReceived; // true if received message and false if sent message
private final HdmiCecMessage mMessage;
+ private final List<String> mSendResults;
- MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
+ MessageHistoryRecord(boolean isReceived, HdmiCecMessage message, List<String> sendResults) {
super();
mIsReceived = isReceived;
mMessage = message;
+ mSendResults = sendResults;
}
@Override
@@ -1674,7 +1742,16 @@
pw.print(" time=");
pw.print(sdf.format(new Date(mTime)));
pw.print(" message=");
- pw.println(mMessage);
+ pw.print(mMessage);
+
+ StringBuilder results = new StringBuilder();
+ if (!mIsReceived && mSendResults != null) {
+ results.append(" (");
+ results.append(String.join(", ", mSendResults));
+ results.append(")");
+ }
+
+ pw.println(results);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index a57292a..18a69c8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -469,9 +469,12 @@
ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
for (HdmiPortInfo info : cecPortInfo) {
if (mhlSupportedPorts.contains(info.getId())) {
- result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
- info.isCecSupported(), true, info.isArcSupported(),
- info.isEarcSupported()));
+ result.add(new HdmiPortInfo.Builder(info.getId(), info.getType(), info.getAddress())
+ .setCecSupported(info.isCecSupported())
+ .setMhlSupported(true)
+ .setArcSupported(info.isArcSupported())
+ .setEarcSupported(info.isEarcSupported())
+ .build());
} else {
result.add(info);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index be4373a..8c13297 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -38,7 +38,9 @@
import android.hardware.SensorPrivacyManager.Sensors;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
+import android.hardware.input.HostUsiVersion;
import android.hardware.input.IInputDeviceBatteryListener;
import android.hardware.input.IInputDeviceBatteryState;
import android.hardware.input.IInputDevicesChangedListener;
@@ -168,6 +170,7 @@
private final Context mContext;
private final InputManagerHandler mHandler;
+ private DisplayManagerInternal mDisplayManagerInternal;
// Context cache used for loading pointer resources.
private Context mPointerIconDisplayContext;
@@ -519,6 +522,8 @@
Slog.d(TAG, "System ready.");
}
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+
synchronized (mLidSwitchLock) {
mSystemReady = true;
@@ -1450,7 +1455,8 @@
}
private void updateMaximumObscuringOpacityForTouchFromSettings() {
- final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch();
+ InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
+ final float opacity = im.getMaximumObscuringOpacityForTouch();
if (opacity < 0 || opacity > 1) {
Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
+ ", it should be >= 0 and <= 1, rejecting update.");
@@ -2254,6 +2260,11 @@
}
@Override
+ public HostUsiVersion getHostUsiVersionFromDisplayConfig(int displayId) {
+ return mDisplayManagerInternal.getHostUsiVersion(displayId);
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 282ad57..262013e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -113,9 +113,8 @@
import com.android.internal.util.HexDump;
import com.android.server.FgThread;
import com.android.server.location.gnss.GnssSatelliteBlocklistHelper.GnssSatelliteBlocklistCallback;
-import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
import com.android.server.location.gnss.hal.GnssNative;
-import com.android.server.location.injector.Injector;
import com.android.server.location.provider.AbstractLocationProvider;
import java.io.FileDescriptor;
@@ -138,7 +137,7 @@
* {@hide}
*/
public class GnssLocationProvider extends AbstractLocationProvider implements
- InjectNtpTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
+ InjectTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
GnssNative.LocationCallbacks, GnssNative.SvStatusCallbacks, GnssNative.AGpsCallbacks,
GnssNative.PsdsCallbacks, GnssNative.NotificationCallbacks,
GnssNative.LocationRequestCallbacks, GnssNative.TimeCallbacks {
@@ -307,7 +306,7 @@
private boolean mSuplEsEnabled = false;
private final LocationExtras mLocationExtras = new LocationExtras();
- private final NtpTimeHelper mNtpTimeHelper;
+ private final NetworkTimeHelper mNetworkTimeHelper;
private final GnssSatelliteBlocklistHelper mGnssSatelliteBlocklistHelper;
// Available only on GNSS HAL 2.0 implementations and later.
@@ -398,7 +397,7 @@
}
}
- public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
+ public GnssLocationProvider(Context context, GnssNative gnssNative,
GnssMetrics gnssMetrics) {
super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
Collections.emptySet());
@@ -470,7 +469,7 @@
GnssLocationProvider.this::onNetworkAvailable,
mHandler.getLooper(), mNIHandler);
- mNtpTimeHelper = new NtpTimeHelper(mContext, mHandler.getLooper(), this);
+ mNetworkTimeHelper = NetworkTimeHelper.create(mContext, mHandler.getLooper(), this);
mGnssSatelliteBlocklistHelper =
new GnssSatelliteBlocklistHelper(mContext,
mHandler.getLooper(), this);
@@ -647,18 +646,19 @@
}
/**
- * Implements {@link InjectNtpTimeCallback#injectTime}
+ * Implements {@link InjectTimeCallback#injectTime}
*/
@Override
- public void injectTime(long time, long timeReference, int uncertainty) {
- mGnssNative.injectTime(time, timeReference, uncertainty);
+ public void injectTime(long unixEpochTimeMillis, long elapsedRealtimeMillis,
+ int uncertaintyMillis) {
+ mGnssNative.injectTime(unixEpochTimeMillis, elapsedRealtimeMillis, uncertaintyMillis);
}
/**
* Implements {@link GnssNetworkConnectivityHandler.GnssNetworkListener#onNetworkAvailable()}
*/
private void onNetworkAvailable() {
- mNtpTimeHelper.onNetworkAvailable();
+ mNetworkTimeHelper.onNetworkAvailable();
// Download only if supported, (prevents an unnecessary on-boot download)
if (mSupportsPsds) {
synchronized (mLock) {
@@ -1145,7 +1145,7 @@
if ("delete_aiding_data".equals(command)) {
deleteAidingData(extras);
} else if ("force_time_injection".equals(command)) {
- requestUtcTime();
+ demandUtcTimeInjection();
} else if ("force_psds_injection".equals(command)) {
if (mSupportsPsds) {
postWithWakeLockHeld(() -> handleDownloadPsdsData(
@@ -1514,9 +1514,9 @@
/* userResponse= */ 0);
}
- private void requestUtcTime() {
- if (DEBUG) Log.d(TAG, "utcTimeRequest");
- postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
+ private void demandUtcTimeInjection() {
+ if (DEBUG) Log.d(TAG, "demandUtcTimeInjection");
+ postWithWakeLockHeld(mNetworkTimeHelper::demandUtcTimeInjection);
}
@@ -1721,9 +1721,16 @@
public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
mHandler.post(() -> {
- if (mGnssNative.getCapabilities().hasOnDemandTime()) {
- mNtpTimeHelper.enablePeriodicTimeInjection();
- requestUtcTime();
+ boolean useOnDemandTimeInjection = mGnssNative.getCapabilities().hasOnDemandTime();
+
+ // b/73893222: There is a historic bug on Android, which means that the capability
+ // "on demand time" is interpreted as "enable periodic injection" elsewhere but an
+ // on-demand injection is done here. GNSS developers may have come to rely on the
+ // periodic behavior, so it has been kept and all methods named to reflect what is
+ // actually done. "On demand" requests are supported regardless of the capability.
+ mNetworkTimeHelper.setPeriodicTimeInjectionMode(useOnDemandTimeInjection);
+ if (useOnDemandTimeInjection) {
+ demandUtcTimeInjection();
}
restartLocationRequest();
@@ -1857,7 +1864,7 @@
@Override
public void onRequestUtcTime() {
- requestUtcTime();
+ demandUtcTimeInjection();
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 69385a9..2174f40 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -83,8 +83,7 @@
mGnssMetrics = new GnssMetrics(mContext, IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME)), mGnssNative);
- mGnssLocationProvider = new GnssLocationProvider(mContext, injector, mGnssNative,
- mGnssMetrics);
+ mGnssLocationProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
mGnssStatusProvider = new GnssStatusProvider(injector, mGnssNative);
mGnssNmeaProvider = new GnssNmeaProvider(injector, mGnssNative);
mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector, mGnssNative);
diff --git a/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
new file mode 100644
index 0000000..72d6f70
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.location.gnss;
+
+import android.content.Context;
+import android.os.Looper;
+
+/**
+ * An abstraction for use by {@link GnssLocationProvider}. This class allows switching between
+ * implementations with a compile-time constant change, which is less risky than rolling back a
+ * whole class. When there is a single implementation again this class can be replaced by that
+ * implementation.
+ */
+abstract class NetworkTimeHelper {
+
+ /**
+ * The callback interface used by {@link NetworkTimeHelper} to report the time to {@link
+ * GnssLocationProvider}. The callback can happen at any time using the thread associated with
+ * the looper passed to {@link #create(Context, Looper, InjectTimeCallback)}.
+ */
+ interface InjectTimeCallback {
+ void injectTime(long unixEpochTimeMillis, long elapsedRealtimeMillis,
+ int uncertaintyMillis);
+ }
+
+ /**
+ * Creates the {@link NetworkTimeHelper} instance for use by {@link GnssLocationProvider}.
+ */
+ static NetworkTimeHelper create(
+ Context context, Looper looper, InjectTimeCallback injectTimeCallback) {
+ return new NtpNetworkTimeHelper(context, looper, injectTimeCallback);
+ }
+
+ /**
+ * Sets the "on demand time injection" mode.
+ *
+ * <p>Called by {@link GnssLocationProvider} to set the expected time injection behavior.
+ * When {@code enablePeriodicTimeInjection == true}, the time helper should periodically send
+ * the time on an undefined schedule. The time can be injected at other times for other reasons
+ * as well as be requested via {@link #demandUtcTimeInjection()}.
+ *
+ * @param periodicTimeInjectionEnabled {@code true} if the GNSS implementation requires periodic
+ * time signals
+ */
+ abstract void setPeriodicTimeInjectionMode(boolean periodicTimeInjectionEnabled);
+
+ /**
+ * Requests an asynchronous time injection via {@link InjectTimeCallback#injectTime}, if a
+ * network time is available. {@link InjectTimeCallback#injectTime} may not be called if a
+ * network time is not available.
+ */
+ abstract void demandUtcTimeInjection();
+
+ /**
+ * Notifies that network connectivity has been established.
+ *
+ * <p>Called by {@link GnssLocationProvider} when the device establishes a data network
+ * connection.
+ */
+ abstract void onNetworkAvailable();
+
+}
diff --git a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java b/services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
similarity index 83%
rename from services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
rename to services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
index e4bc788..479dbda 100644
--- a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
+++ b/services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
@@ -30,14 +30,11 @@
import com.android.internal.annotations.VisibleForTesting;
/**
- * Handles inject NTP time to GNSS.
- *
- * <p>The client is responsible to call {@link #onNetworkAvailable()} when network is available
- * for retrieving NTP Time.
+ * Handles injecting network time to GNSS by explicitly making NTP requests when needed.
*/
-class NtpTimeHelper {
+class NtpNetworkTimeHelper extends NetworkTimeHelper {
- private static final String TAG = "NtpTimeHelper";
+ private static final String TAG = "NtpNetworkTimeHelper";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// states for injecting ntp
@@ -71,23 +68,19 @@
private final WakeLock mWakeLock;
private final Handler mHandler;
- private final InjectNtpTimeCallback mCallback;
+ private final InjectTimeCallback mCallback;
// flags to trigger NTP when network becomes available
// initialized to STATE_PENDING_NETWORK so we do NTP when the network comes up after booting
@GuardedBy("this")
private int mInjectNtpTimeState = STATE_PENDING_NETWORK;
- // set to true if the GPS engine requested on-demand NTP time requests
+ // Enables periodic time injection in addition to injection for other reasons.
@GuardedBy("this")
- private boolean mOnDemandTimeInjection;
-
- interface InjectNtpTimeCallback {
- void injectTime(long time, long timeReference, int uncertainty);
- }
+ private boolean mPeriodicTimeInjection;
@VisibleForTesting
- NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback,
+ NtpNetworkTimeHelper(Context context, Looper looper, InjectTimeCallback callback,
NtpTrustedTime ntpTime) {
mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
mCallback = callback;
@@ -97,14 +90,23 @@
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
}
- NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback) {
+ NtpNetworkTimeHelper(Context context, Looper looper, InjectTimeCallback callback) {
this(context, looper, callback, NtpTrustedTime.getInstance(context));
}
- synchronized void enablePeriodicTimeInjection() {
- mOnDemandTimeInjection = true;
+ @Override
+ synchronized void setPeriodicTimeInjectionMode(boolean periodicTimeInjectionEnabled) {
+ if (periodicTimeInjectionEnabled) {
+ mPeriodicTimeInjection = true;
+ }
}
+ @Override
+ void demandUtcTimeInjection() {
+ retrieveAndInjectNtpTime();
+ }
+
+ @Override
synchronized void onNetworkAvailable() {
if (mInjectNtpTimeState == STATE_PENDING_NETWORK) {
retrieveAndInjectNtpTime();
@@ -120,7 +122,7 @@
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
- synchronized void retrieveAndInjectNtpTime() {
+ private synchronized void retrieveAndInjectNtpTime() {
if (mInjectNtpTimeState == STATE_RETRIEVING_AND_INJECTING) {
// already downloading data
return;
@@ -166,18 +168,15 @@
if (DEBUG) {
Log.d(TAG, String.format(
- "onDemandTimeInjection=%s, refreshSuccess=%s, delay=%s",
- mOnDemandTimeInjection,
+ "mPeriodicTimeInjection=%s, refreshSuccess=%s, delay=%s",
+ mPeriodicTimeInjection,
refreshSuccess,
delay));
}
- // TODO(b/73893222): reconcile Capabilities bit 'on demand' name vs. de facto periodic
- // injection.
- if (mOnDemandTimeInjection || !refreshSuccess) {
- /* Schedule next NTP injection.
- * Since this is delayed, the wake lock is released right away, and will be held
- * again when the delayed task runs.
- */
+ if (mPeriodicTimeInjection || !refreshSuccess) {
+ // Schedule next NTP injection.
+ // Since this is delayed, the wake lock is released right away, and will be held
+ // again when the delayed task runs.
mHandler.postDelayed(this::retrieveAndInjectNtpTime, delay);
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 31b8ef2..48e0c30 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -114,6 +114,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -2503,6 +2504,28 @@
return mRecoverableKeyStoreManager.getKey(alias);
}
+ /**
+ * Starts a session to verify lock screen credentials provided by a remote device.
+ */
+ public void startRemoteLockscreenValidation() {
+ if (!FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API)) {
+ throw new UnsupportedOperationException("Under development");
+ }
+ mRecoverableKeyStoreManager.startRemoteLockscreenValidation();
+ }
+
+ /**
+ * Verifies credentials guess from a remote device.
+ */
+ public void validateRemoteLockscreen(@NonNull byte[] encryptedCredential) {
+ if (!FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API)) {
+ throw new UnsupportedOperationException("Under development");
+ }
+ mRecoverableKeyStoreManager.validateRemoteLockscreen(encryptedCredential);
+ }
+
// Reading these settings needs the contacts permission
private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index 7921619..2818caa 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index e620c80..b437421 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -44,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.security.SecureBox;
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
@@ -965,6 +966,35 @@
}
}
+ /**
+ * Starts a session to verify lock screen credentials provided by a remote device.
+ */
+ public void startRemoteLockscreenValidation() {
+ checkVerifyRemoteLockscreenPermission();
+ // TODO(b/254335492): Create session in memory
+ return;
+ }
+
+ /**
+ * Verifies encrypted credentials guess from a remote device.
+ */
+ public void validateRemoteLockscreen(@NonNull byte[] encryptedCredential) {
+ checkVerifyRemoteLockscreenPermission();
+ // TODO(b/254335492): Decrypt and verify credentials
+ return;
+ }
+
+ private void checkVerifyRemoteLockscreenPermission() {
+ // TODO(b/254335492): Check new system permission
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.RECOVER_KEYSTORE,
+ "Caller " + Binder.getCallingUid()
+ + " doesn't have verifyRemoteLockscreen permission.");
+ int userId = UserHandle.getCallingUserId();
+ int uid = Binder.getCallingUid();
+ mCleanupManager.registerRecoveryAgent(userId, uid);
+ }
+
private void checkRecoverKeyStorePermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.RECOVER_KEYSTORE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index 1dffcf9..4a17e9a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -78,6 +78,18 @@
return new RecoverableKeyStoreDb(helper);
}
+ /**
+ * A new instance, storing the database in the user directory of {@code context}.
+ *
+ * @hide
+ */
+ public static RecoverableKeyStoreDb newInstance(Context context, int version) {
+ RecoverableKeyStoreDbHelper helper = new RecoverableKeyStoreDbHelper(context, version);
+ helper.setWriteAheadLoggingEnabled(true);
+ helper.setIdleConnectionTimeout(IDLE_TIMEOUT_SECONDS);
+ return new RecoverableKeyStoreDb(helper);
+ }
+
private RecoverableKeyStoreDb(RecoverableKeyStoreDbHelper keyStoreDbHelper) {
this.mKeyStoreDbHelper = keyStoreDbHelper;
this.mTestOnlyInsecureCertificateHelper = new TestOnlyInsecureCertificateHelper();
@@ -389,7 +401,55 @@
ensureUserMetadataEntryExists(userId);
return db.update(UserMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
+ }
+ /**
+ * Sets the {@code badGuessCounter} for the user {@code userId}.
+ *
+ * @return The number of updated rows.
+ */
+ public long setBadRemoteGuessCounter(int userId, int badGuessCounter) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
+ values.put(UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER, badGuessCounter);
+ String selection = UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+ String[] selectionArguments = new String[] {String.valueOf(userId)};
+
+ ensureUserMetadataEntryExists(userId);
+ return db.update(UserMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
+ }
+
+ /**
+ * Returns the number of invalid remote lock screen guesses for the user {@code userId}.
+ */
+ public int getBadRemoteGuessCounter(int userId) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+ String[] projection = {
+ UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER};
+ String selection =
+ UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+ String[] selectionArguments = {
+ Integer.toString(userId)};
+
+ try (
+ Cursor cursor = db.query(
+ UserMetadataEntry.TABLE_NAME,
+ projection,
+ selection,
+ selectionArguments,
+ /*groupBy=*/ null,
+ /*having=*/ null,
+ /*orderBy=*/ null)
+ ) {
+ if (cursor.getCount() == 0) {
+ return 0;
+ }
+ cursor.moveToFirst();
+ return cursor.getInt(
+ cursor.getColumnIndexOrThrow(
+ UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER));
+ }
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index e79d117..684f49f 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -98,6 +98,11 @@
* Serial number for the user which can not be reused. Default value is {@code -1}.
*/
static final String COLUMN_NAME_USER_SERIAL_NUMBER = "user_serial_number";
+
+ /**
+ * Number of invalid lockscreen credentials guess from a remote device.
+ */
+ static final String COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER = "bad_remote_guess_counter";
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index 3e7b0a7..fa53a607 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -32,7 +32,10 @@
class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
private static final String TAG = "RecoverableKeyStoreDbHp";
- static final int DATABASE_VERSION = 6; // Added user id serial number.
+ // v6 - added user id serial number.
+ static final int DATABASE_VERSION = 6;
+ // v7 - added bad guess counter for remote LSKF check;
+ static final int DATABASE_VERSION_7 = 7;
private static final String DATABASE_NAME = "recoverablekeystore.db";
private static final String SQL_CREATE_KEYS_ENTRY =
@@ -57,6 +60,16 @@
+ UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER,"
+ UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER + " INTEGER DEFAULT -1)";
+
+ private static final String SQL_CREATE_USER_METADATA_ENTRY_FOR_V7 =
+ "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
+ + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
+ + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
+ + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER,"
+ + UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER + " INTEGER DEFAULT -1,"
+ + UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER
+ + " INTEGER DEFAULT 0)";
+
private static final String SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY =
"CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
+ RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY,"
@@ -101,13 +114,26 @@
"DROP TABLE IF EXISTS " + RootOfTrustEntry.TABLE_NAME;
RecoverableKeyStoreDbHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ super(context, DATABASE_NAME, null, getDbVersion(context));
+ }
+
+ RecoverableKeyStoreDbHelper(Context context, int version) {
+ super(context, DATABASE_NAME, null, version);
+ }
+
+ private static int getDbVersion(Context context) {
+ // TODO(b/254335492): Check flag
+ return DATABASE_VERSION;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_KEYS_ENTRY);
- db.execSQL(SQL_CREATE_USER_METADATA_ENTRY);
+ if (db.getVersion() == 6) {
+ db.execSQL(SQL_CREATE_USER_METADATA_ENTRY);
+ } else {
+ db.execSQL(SQL_CREATE_USER_METADATA_ENTRY_FOR_V7);
+ }
db.execSQL(SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY);
db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
}
@@ -147,6 +173,11 @@
oldVersion = 6;
}
+ if (oldVersion < 7 && newVersion >= 7) {
+ upgradeDbForVersion7(db);
+ oldVersion = 7;
+ }
+
if (oldVersion != newVersion) {
Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version");
}
@@ -194,6 +225,14 @@
/*defaultStr=*/ null);
}
+ private void upgradeDbForVersion7(SQLiteDatabase db) {
+ Log.d(TAG, "Updating recoverable keystore database to version 7");
+ addColumnToTable(db, UserMetadataEntry.TABLE_NAME,
+ UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER,
+ "INTEGER DEFAULT 0",
+ /*defaultStr=*/ null);
+ }
+
private static void addColumnToTable(
SQLiteDatabase db, String tableName, String column, String columnType,
String defaultStr) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
new file mode 100644
index 0000000..267a72e
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.locksettings.recoverablekeystore.storage;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.security.SecureBox;
+
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+
+/**
+ * Memory based storage for keyPair used to send encrypted credentials from a remote device.
+ *
+ * @hide
+ */
+public class RemoteLockscreenValidationSessionStorage {
+
+ private static final long SESSION_TIMEOUT_MILLIS = 10L * DateUtils.MINUTE_IN_MILLIS;
+ private static final String TAG = "RemoteLockscreenValidation";
+
+ @VisibleForTesting
+ final SparseArray<LockscreenVerificationSession> mSessionsByUserId =
+ new SparseArray<>(0);
+
+ /**
+ * Returns session for given user or null.
+ *
+ * @param userId The user id
+ * @return The session info.
+ *
+ * @hide
+ */
+ @Nullable
+ public LockscreenVerificationSession get(int userId) {
+ synchronized (mSessionsByUserId) {
+ return mSessionsByUserId.get(userId);
+ }
+ }
+
+ /**
+ * Creates a new session to verify credentials guess.
+ *
+ * Session will be automatically removed after 10 minutes of inactivity.
+ * @param userId The user id
+ *
+ * @hide
+ */
+ public LockscreenVerificationSession startSession(int userId) {
+ synchronized (mSessionsByUserId) {
+ if (mSessionsByUserId.get(userId) != null) {
+ mSessionsByUserId.delete(userId);
+ }
+
+ KeyPair newKeyPair;
+ try {
+ newKeyPair = SecureBox.genKeyPair();
+ } catch (NoSuchAlgorithmException e) {
+ // impossible
+ throw new RuntimeException(e);
+ }
+ LockscreenVerificationSession newSession =
+ new LockscreenVerificationSession(newKeyPair, SystemClock.elapsedRealtime());
+ mSessionsByUserId.put(userId, newSession);
+ return newSession;
+ }
+ }
+
+ /**
+ * Deletes session for a user.
+ */
+ public void finishSession(int userId) {
+ synchronized (mSessionsByUserId) {
+ mSessionsByUserId.delete(userId);
+ }
+ }
+
+ /**
+ * Creates a task which deletes expired sessions.
+ */
+ public Runnable getLockscreenValidationCleanupTask() {
+ return new LockscreenValidationCleanupTask();
+ }
+
+ /**
+ * Holder for KeyPair used by remote lock screen validation.
+ *
+ * @hide
+ */
+ public class LockscreenVerificationSession {
+ private final KeyPair mKeyPair;
+ private final long mElapsedStartTime;
+
+ /**
+ * @hide
+ */
+ LockscreenVerificationSession(KeyPair keyPair, long elapsedStartTime) {
+ mKeyPair = keyPair;
+ mElapsedStartTime = elapsedStartTime;
+ }
+
+ /**
+ * Returns SecureBox key pair.
+ */
+ public KeyPair getKeyPair() {
+ return mKeyPair;
+ }
+
+ /**
+ * Time when the session started.
+ */
+ private long getElapsedStartTimeMillis() {
+ return mElapsedStartTime;
+ }
+ }
+
+ private class LockscreenValidationCleanupTask implements Runnable {
+ @Override
+ public void run() {
+ try {
+ synchronized (mSessionsByUserId) {
+ ArrayList<Integer> keysToRemove = new ArrayList<>();
+ for (int i = 0; i < mSessionsByUserId.size(); i++) {
+ long now = SystemClock.elapsedRealtime();
+ long startTime = mSessionsByUserId.valueAt(i).getElapsedStartTimeMillis();
+ if (now - startTime > SESSION_TIMEOUT_MILLIS) {
+ int userId = mSessionsByUserId.keyAt(i);
+ keysToRemove.add(userId);
+ }
+ }
+ for (Integer userId : keysToRemove) {
+ mSessionsByUserId.delete(userId);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unexpected exception thrown during LockscreenValidationCleanupTask", e);
+ }
+ }
+
+ }
+
+}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3ad0e44..a9b9b54 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -69,6 +69,7 @@
import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -247,6 +248,24 @@
// Binder call
@Override
+ public void showMediaOutputSwitcher(String packageName) {
+ if (!validatePackageName(Binder.getCallingUid(), packageName)) {
+ throw new SecurityException("packageName must match the calling identity");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ StatusBarManagerInternal statusBar =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ statusBar.showMediaOutputSwitcher(packageName);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
public MediaRouterClientState getState(IMediaRouterClient client) {
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index e9ee750..25efe0c 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -64,7 +64,7 @@
* The {@link MediaProjectionManagerService} manages the creation and lifetime of MediaProjections,
* as well as the capabilities they grant. Any service using MediaProjection tokens as permission
* grants <b>must</b> validate the token before use by calling {@link
- * IMediaProjectionService#isValidMediaProjection}.
+ * IMediaProjectionService#isCurrentProjection}.
*/
public final class MediaProjectionManagerService extends SystemService
implements Watchdog.Monitor {
@@ -228,7 +228,7 @@
mCallbackDelegate.dispatchStop(projection);
}
- private boolean isValidMediaProjection(IBinder token) {
+ private boolean isCurrentProjection(IBinder token) {
synchronized (mLock) {
if (mProjectionToken != null) {
return mProjectionToken.equals(token);
@@ -313,8 +313,8 @@
}
@Override // Binder call
- public boolean isValidMediaProjection(IMediaProjection projection) {
- return MediaProjectionManagerService.this.isValidMediaProjection(
+ public boolean isCurrentProjection(IMediaProjection projection) {
+ return MediaProjectionManagerService.this.isCurrentProjection(
projection == null ? null : projection.asBinder());
}
@@ -357,7 +357,7 @@
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to notify "
+ "on captured content resize");
}
- if (!isValidMediaProjection(mProjectionGrant)) {
+ if (!isCurrentProjection(mProjectionGrant)) {
return;
}
final long token = Binder.clearCallingIdentity();
@@ -377,7 +377,7 @@
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to notify "
+ "on captured content resize");
}
- if (!isValidMediaProjection(mProjectionGrant)) {
+ if (!isCurrentProjection(mProjectionGrant)) {
return;
}
final long token = Binder.clearCallingIdentity();
@@ -429,8 +429,9 @@
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- if (!isValidMediaProjection(projection)) {
- throw new SecurityException("Invalid media projection");
+ if (!isCurrentProjection(projection)) {
+ throw new SecurityException("Unable to set ContentRecordingSession on "
+ + "non-current MediaProjection");
}
if (!LocalServices.getService(
WindowManagerInternal.class).setContentRecordingSession(
@@ -536,7 +537,7 @@
throw new IllegalArgumentException("callback must not be null");
}
synchronized (mLock) {
- if (isValidMediaProjection(asBinder())) {
+ if (isCurrentProjection(asBinder())) {
Slog.w(TAG, "UID " + Binder.getCallingUid()
+ " attempted to start already started MediaProjection");
return;
@@ -603,7 +604,7 @@
@Override // Binder call
public void stop() {
synchronized (mLock) {
- if (!isValidMediaProjection(asBinder())) {
+ if (!isCurrentProjection(asBinder())) {
Slog.w(TAG, "Attempted to stop inactive MediaProjection "
+ "(uid=" + Binder.getCallingUid() + ", "
+ "pid=" + Binder.getCallingPid() + ")");
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 004caf3..669ea95 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -150,8 +150,9 @@
= new ArraySet<>();
// Just the packages from mEnabledServicesForCurrentProfiles
private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
- // List of enabled packages that have nevertheless asked not to be run
- private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
+ // Per user id, list of enabled packages that have nevertheless asked not to be run
+ private final android.util.SparseSetArray<ComponentName> mSnoozing =
+ new android.util.SparseSetArray<>();
// List of approved packages or components (by user, then by primary/secondary) that are
// allowed to be bound as managed services. A package or component appearing in this list does
@@ -386,10 +387,15 @@
}
}
- pw.println(" Snoozed " + getCaption() + "s (" +
- mSnoozingForCurrentProfiles.size() + "):");
- for (ComponentName name : mSnoozingForCurrentProfiles) {
- pw.println(" " + name.flattenToShortString());
+ synchronized (mSnoozing) {
+ pw.println(" Snoozed " + getCaption() + "s ("
+ + mSnoozing.size() + "):");
+ for (int i = 0; i < mSnoozing.size(); i++) {
+ pw.println(" User: " + mSnoozing.keyAt(i));
+ for (ComponentName name : mSnoozing.valuesAt(i)) {
+ pw.println(" " + name.flattenToShortString());
+ }
+ }
}
}
@@ -431,8 +437,16 @@
}
}
- for (ComponentName name : mSnoozingForCurrentProfiles) {
- name.dumpDebug(proto, ManagedServicesProto.SNOOZED);
+ synchronized (mSnoozing) {
+ for (int i = 0; i < mSnoozing.size(); i++) {
+ long token = proto.start(ManagedServicesProto.SNOOZED);
+ proto.write(ManagedServicesProto.SnoozedServices.USER_ID,
+ mSnoozing.keyAt(i));
+ for (ComponentName name : mSnoozing.valuesAt(i)) {
+ name.dumpDebug(proto, ManagedServicesProto.SnoozedServices.SNOOZED);
+ }
+ proto.end(token);
+ }
}
}
@@ -975,6 +989,9 @@
synchronized (mApproved) {
mApproved.remove(user);
}
+ synchronized (mSnoozing) {
+ mSnoozing.remove(user);
+ }
rebindServices(true, user);
}
@@ -1066,15 +1083,17 @@
}
protected void setComponentState(ComponentName component, int userId, boolean enabled) {
- boolean previous = !mSnoozingForCurrentProfiles.contains(component);
- if (previous == enabled) {
- return;
- }
+ synchronized (mSnoozing) {
+ boolean previous = !mSnoozing.contains(userId, component);
+ if (previous == enabled) {
+ return;
+ }
- if (enabled) {
- mSnoozingForCurrentProfiles.remove(component);
- } else {
- mSnoozingForCurrentProfiles.add(component);
+ if (enabled) {
+ mSnoozing.remove(userId, component);
+ } else {
+ mSnoozing.add(userId, component);
+ }
}
// State changed
@@ -1287,7 +1306,10 @@
}
final Set<ComponentName> add = new HashSet<>(userComponents);
- add.removeAll(mSnoozingForCurrentProfiles);
+ ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
+ if (snoozed != null) {
+ add.removeAll(snoozed);
+ }
componentsToBind.put(userId, add);
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index ff6420c..a610b5b 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -430,8 +430,9 @@
* checks.
*/
@Override
- public void setInteractAcrossProfilesAppOp(String packageName, @Mode int newMode) {
- setInteractAcrossProfilesAppOp(packageName, newMode, mInjector.getCallingUserId());
+ public void setInteractAcrossProfilesAppOp(
+ @UserIdInt int userId, String packageName, @Mode int newMode) {
+ setInteractAcrossProfilesAppOp(packageName, newMode, userId);
}
private void setInteractAcrossProfilesAppOp(
@@ -594,8 +595,12 @@
}
@Override
- public boolean canConfigureInteractAcrossProfiles(String packageName) {
- return canConfigureInteractAcrossProfiles(packageName, mInjector.getCallingUserId());
+ public boolean canConfigureInteractAcrossProfiles(int userId, String packageName) {
+ if (mInjector.getCallingUserId() != userId) {
+ mContext.checkCallingOrSelfPermission(INTERACT_ACROSS_USERS);
+ }
+
+ return canConfigureInteractAcrossProfiles(packageName, userId);
}
private boolean canConfigureInteractAcrossProfiles(String packageName, @UserIdInt int userId) {
@@ -613,9 +618,13 @@
}
@Override
- public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
+ public boolean canUserAttemptToConfigureInteractAcrossProfiles(int userId, String packageName) {
+ if (mInjector.getCallingUserId() != userId) {
+ mContext.checkCallingOrSelfPermission(INTERACT_ACROSS_USERS);
+ }
+
return canUserAttemptToConfigureInteractAcrossProfiles(
- packageName, mInjector.getCallingUserId());
+ packageName, userId);
}
private boolean canUserAttemptToConfigureInteractAcrossProfiles(
@@ -676,28 +685,31 @@
}
@Override
- public void resetInteractAcrossProfilesAppOps(List<String> packageNames) {
- packageNames.forEach(this::resetInteractAcrossProfilesAppOp);
+ public void resetInteractAcrossProfilesAppOps(@UserIdInt int userId, List<String> packageNames) {
+ for (String packageName : packageNames) {
+ resetInteractAcrossProfilesAppOp(userId, packageName);
+ }
}
- private void resetInteractAcrossProfilesAppOp(String packageName) {
- if (canConfigureInteractAcrossProfiles(packageName)) {
+ private void resetInteractAcrossProfilesAppOp(@UserIdInt int userId, String packageName) {
+ if (canConfigureInteractAcrossProfiles(packageName, userId)) {
Slog.w(TAG, "Not resetting app-op for package " + packageName
+ " since it is still configurable by users.");
return;
}
final String op =
AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES);
- setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op));
+ setInteractAcrossProfilesAppOp(userId, packageName, AppOpsManager.opToDefaultMode(op));
}
@Override
- public void clearInteractAcrossProfilesAppOps() {
+ public void clearInteractAcrossProfilesAppOps(@UserIdInt int userId) {
final int defaultMode =
AppOpsManager.opToDefaultMode(
AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES));
findAllPackageNames()
- .forEach(packageName -> setInteractAcrossProfilesAppOp(packageName, defaultMode));
+ .forEach(packageName -> setInteractAcrossProfilesAppOp(
+ userId, packageName, defaultMode));
}
private List<String> findAllPackageNames() {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
index c58128a..9683469 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -30,7 +30,7 @@
* Representation of an immutable default cross-profile intent filter.
*/
@Immutable
-final class DefaultCrossProfileIntentFilter {
+public final class DefaultCrossProfileIntentFilter {
@IntDef({
Direction.TO_PARENT,
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index ceaaefd..3799193 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -321,6 +321,15 @@
}
/**
+ * Returns default telephony related intent filters for managed profile.
+ */
+ public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileTelephonyFilters() {
+ return Arrays.asList(
+ DIAL_DATA,
+ SMS_MMS);
+ }
+
+ /**
* Clone profile's DefaultCrossProfileIntentFilter
*/
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3df46a2..e0de294 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -43,6 +43,7 @@
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
@@ -774,6 +775,25 @@
if (!deleteAllUsers) {
returnCode = deletePackageX(internalPackageName, versionCode,
userId, deleteFlags, false /*removedBySystem*/);
+
+ // Get a list of child user profiles and delete if package is
+ // present in that profile.
+ int[] childUserIds = mUserManagerInternal.getProfileIds(userId, true);
+ int returnCodeOfChild;
+ for (int childId : childUserIds) {
+ if (childId == userId) continue;
+ UserProperties userProperties = mUserManagerInternal
+ .getUserProperties(childId);
+ if (userProperties != null && userProperties.getDeleteAppWithParent()) {
+ returnCodeOfChild = deletePackageX(internalPackageName, versionCode,
+ childId, deleteFlags, false /*removedBySystem*/);
+ if (returnCodeOfChild != PackageManager.DELETE_SUCCEEDED) {
+ Slog.w(TAG, "Package delete failed for user " + childId
+ + ", returnCode " + returnCodeOfChild);
+ returnCode = PackageManager.DELETE_FAILED_FOR_CHILD_PROFILE;
+ }
+ }
+ }
} else {
int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot,
internalPackageName, users);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c38b822..c5bcddc 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -17,6 +17,8 @@
package com.android.server.pm;
import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME;
+import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
@@ -1143,7 +1145,8 @@
final int code;
try {
code = mActivityTaskManagerInternal.startActivitiesAsPackage(publisherPackage,
- publishedFeatureId, userId, intents, startActivityOptions);
+ publishedFeatureId, userId, intents,
+ getActivityOptionsForLauncher(startActivityOptions));
if (ActivityManager.isStartResultSuccessful(code)) {
return true; // Success
} else {
@@ -1158,6 +1161,23 @@
}
}
+ private Bundle getActivityOptionsForLauncher(Bundle startActivityOptions) {
+ // starting a shortcut implies the user's consent, so grant the launchers/senders BAL
+ // privileges (unless the caller explicitly defined the behavior)
+ if (startActivityOptions == null) {
+ return ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle();
+ }
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(startActivityOptions);
+ if (activityOptions.getPendingIntentBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ // only override if the property was not explicitly set
+ return activityOptions.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle();
+ }
+ return startActivityOptions;
+ }
+
@Override
public boolean isActivityEnabled(
String callingPackage, ComponentName component, UserHandle user)
@@ -1216,8 +1236,8 @@
i.setSourceBounds(sourceBounds);
mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
- callingFeatureId, i, /* resultTo= */ null, Intent.FLAG_ACTIVITY_NEW_TASK, opts,
- userId);
+ callingFeatureId, i, /* resultTo= */ null, Intent.FLAG_ACTIVITY_NEW_TASK,
+ getActivityOptionsForLauncher(opts), userId);
}
@Override
@@ -1264,7 +1284,8 @@
mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
callingFeatureId, launchIntent, /* resultTo= */ null,
- Intent.FLAG_ACTIVITY_NEW_TASK, opts, user.getIdentifier());
+ Intent.FLAG_ACTIVITY_NEW_TASK, getActivityOptionsForLauncher(opts),
+ user.getIdentifier());
}
/**
@@ -1347,7 +1368,7 @@
}
mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
callingFeatureId, intent, /* resultTo= */ null, Intent.FLAG_ACTIVITY_NEW_TASK,
- opts, user.getIdentifier());
+ getActivityOptionsForLauncher(opts), user.getIdentifier());
}
/** Checks if user is a profile of or same as listeningUser.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 94e96b0..620ee37 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -187,6 +187,7 @@
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
+import com.android.server.IntentResolver;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
@@ -4745,6 +4746,44 @@
}
@Override
+ public boolean removeCrossProfileIntentFilter(IntentFilter intentFilter,
+ String ownerPackage,
+ int sourceUserId,
+ int targetUserId, int flags) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ final int callingUid = Binder.getCallingUid();
+ enforceOwnerRights(snapshotComputer(), ownerPackage, callingUid);
+ mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId,
+ callingUid, /* addCrossProfileIntentFilter */ false);
+ PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+
+ boolean removedMatchingFilter = false;
+ synchronized (mLock) {
+ CrossProfileIntentResolver resolver =
+ mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
+
+ ArraySet<CrossProfileIntentFilter> set =
+ new ArraySet<>(resolver.filterSet());
+ for (CrossProfileIntentFilter filter : set) {
+ if (IntentResolver.filterEquals(filter.mFilter, intentFilter)
+ && filter.getOwnerPackage().equals(ownerPackage)
+ && filter.getTargetUserId() == targetUserId
+ && filter.getFlags() == flags) {
+ resolver.removeFilter(filter);
+ removedMatchingFilter = true;
+ break;
+ }
+ }
+ }
+ if (removedMatchingFilter) {
+ scheduleWritePackageRestrictions(sourceUserId);
+ }
+ return removedMatchingFilter;
+ }
+
+ @Override
public final void deleteApplicationCacheFiles(final String packageName,
final IPackageDataObserver observer) {
final int userId = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ad8e35d..b919330 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1152,8 +1152,8 @@
throw new IOException("Root has not present");
}
return ApkChecksums.verityHashForFile(new File(filename), hashInfo.rawRootHash);
- } catch (IOException ignore) {
- Slog.e(TAG, "ERROR: could not load root hash from incremental install");
+ } catch (IOException e) {
+ Slog.i(TAG, "Could not obtain verity root hash", e);
}
return null;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 1787116..3c5f309 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -27,6 +27,8 @@
import android.os.UserManager;
import android.util.DebugUtils;
+import com.android.internal.annotations.Keep;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -47,9 +49,10 @@
public @interface OwnerType {
}
- public static final int USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE = 1;
- public static final int USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE = 2;
- public static final int USER_ASSIGNMENT_RESULT_FAILURE = -1;
+ // TODO(b/248408342): Move keep annotation to the method referencing these fields reflectively.
+ @Keep public static final int USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE = 1;
+ @Keep public static final int USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE = 2;
+ @Keep public static final int USER_ASSIGNMENT_RESULT_FAILURE = -1;
private static final String PREFIX_USER_ASSIGNMENT_RESULT = "USER_ASSIGNMENT_RESULT_";
@IntDef(flag = false, prefix = {PREFIX_USER_ASSIGNMENT_RESULT}, value = {
@@ -59,9 +62,10 @@
})
public @interface UserAssignmentResult {}
- public static final int USER_START_MODE_FOREGROUND = 1;
- public static final int USER_START_MODE_BACKGROUND = 2;
- public static final int USER_START_MODE_BACKGROUND_VISIBLE = 3;
+ // TODO(b/248408342): Move keep annotation to the method referencing these fields reflectively.
+ @Keep public static final int USER_START_MODE_FOREGROUND = 1;
+ @Keep public static final int USER_START_MODE_BACKGROUND = 2;
+ @Keep public static final int USER_START_MODE_BACKGROUND_VISIBLE = 3;
private static final String PREFIX_USER_START_MODE = "USER_START_MODE_";
@IntDef(flag = false, prefix = {PREFIX_USER_START_MODE}, value = {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1c83880..762d1f6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -262,7 +262,7 @@
// We need to keep process uid within Integer.MAX_VALUE.
@VisibleForTesting
- static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+ static final int MAX_USER_ID = UserHandle.MAX_SECONDARY_USER_ID;
// Max size of the queue of recently removed users
@VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
index 6e86123..5bdcbb9 100644
--- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
+++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
@@ -36,6 +36,7 @@
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import com.android.internal.os.RoSystemProperties;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.UiThread;
@@ -100,6 +101,11 @@
pw.println(" --reboot (which does a full reboot) or");
pw.println(" --no-restart (which requires a manual restart)");
pw.println();
+ pw.println(" is-headless-system-user-mode [-v | --verbose]");
+ pw.println(" Checks whether the device uses headless system user mode.");
+ pw.println(" It returns the effective mode, even when using emulation");
+ pw.println(" (to get the real mode as well, use -v or --verbose)");
+ pw.println();
pw.println(" is-user-visible [--display DISPLAY_ID] <USER_ID>");
pw.println(" Checks if the given user is visible in the given display.");
pw.println(" If the display option is not set, it uses the user's context to check");
@@ -121,6 +127,8 @@
return runReportPackageAllowlistProblems();
case "set-system-user-mode-emulation":
return runSetSystemUserModeEmulation();
+ case "is-headless-system-user-mode":
+ return runIsHeadlessSystemUserMode();
case "is-user-visible":
return runIsUserVisible();
default:
@@ -407,6 +415,35 @@
return 0;
}
+ private int runIsHeadlessSystemUserMode() {
+ PrintWriter pw = getOutPrintWriter();
+
+ boolean verbose = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ case "--verbose":
+ verbose = true;
+ break;
+ default:
+ pw.println("Invalid option: " + opt);
+ return -1;
+ }
+ }
+
+ boolean isHsum = mService.isHeadlessSystemUserMode();
+ if (!verbose) {
+ // NOTE: do not change output below, as it's used by ITestDevice
+ // (it's ok to change the verbose option though)
+ pw.println(isHsum);
+ } else {
+ pw.printf("effective=%b real=%b\n", isHsum,
+ RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER);
+ }
+ return 0;
+ }
+
/**
* Gets the {@link UserManager} associated with the context of the given user.
*/
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index d56ee30..77c32ae 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -134,9 +134,9 @@
UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
.setCrossProfileIntentResolutionStrategy(UserProperties
.CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
- .setIsMediaSharedWithParent(true)
- .setIsCredentialSharableWithParent(true)
- );
+ .setMediaSharedWithParent(true)
+ .setCredentialShareableWithParent(true)
+ .setDeleteAppWithParent(true));
}
/**
@@ -172,7 +172,7 @@
.setStartWithParent(true)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
.setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
- .setIsCredentialSharableWithParent(true));
+ .setCredentialShareableWithParent(true));
}
/**
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 58f88c3..e74b459 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -206,6 +206,8 @@
static {
SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE);
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND);
}
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8973adc..287cc29 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.pm.permission;
+import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.CAPTURE_AUDIO_OUTPUT;
import static android.Manifest.permission.RECORD_AUDIO;
@@ -1313,7 +1314,9 @@
// Bg location is one-off runtime modifier permission and has no app op
if (sPlatformPermissions.containsKey(permission)
&& !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)
- && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)) {
+ && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)
+ && !Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND
+ .equals(permission)) {
Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
+ " with no app op defined!");
}
@@ -1377,14 +1380,15 @@
boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
uid) == PackageManager.PERMISSION_GRANTED;
- // Override certain permissions checks for the HotwordDetectionService. This service is
- // an isolated service, which ordinarily cannot hold permissions.
+ // Override certain permissions checks for the shared isolated process for both
+ // HotwordDetectionService and VisualQueryDetectionService, which ordinarily cannot hold
+ // any permissions.
// There's probably a cleaner, more generalizable way to do this. For now, this is
// the only use case for this, so simply override here.
if (!permissionGranted
&& Process.isIsolated(uid) // simple check which fails-fast for the common case
&& (permission.equals(RECORD_AUDIO) || permission.equals(CAPTURE_AUDIO_HOTWORD)
- || permission.equals(CAPTURE_AUDIO_OUTPUT))) {
+ || permission.equals(CAPTURE_AUDIO_OUTPUT) || permission.equals(CAMERA))) {
HotwordDetectionServiceProvider hotwordServiceProvider =
permissionManagerServiceInt.getHotwordDetectionServiceProvider();
permissionGranted = hotwordServiceProvider != null
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 383249f..401eac6 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -447,7 +447,8 @@
// package, so we don't need to modify it.
if (Process.isIsolated(uid) // simple check which fails-fast for the common case
&& (code == AppOpsManager.OP_RECORD_AUDIO
- || code == AppOpsManager.OP_RECORD_AUDIO_HOTWORD)) {
+ || code == AppOpsManager.OP_RECORD_AUDIO_HOTWORD
+ || code == AppOpsManager.OP_CAMERA)) {
final HotwordDetectionServiceIdentity hotwordDetectionServiceIdentity =
mVoiceInteractionManagerInternal.getHotwordDetectionServiceIdentity();
if (hotwordDetectionServiceIdentity != null
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5a0c344..e0bcc0e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -991,14 +991,7 @@
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
Slog.d(TAG, "No behavior defined for power press count " + count);
- } else if (count == 1 && interactive) {
- if (beganFromNonInteractive) {
- // The "screen is off" case, where we might want to start dreaming on power button
- // press.
- attemptToDreamFromShortPowerButtonPress(false, () -> {});
- return;
- }
-
+ } else if (count == 1 && interactive && !beganFromNonInteractive) {
if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
Slog.i(TAG, "Suppressing power key because the user is interacting with the "
+ "fingerprint sensor");
@@ -3777,9 +3770,6 @@
private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
mKeyguardOccludedChanged = false;
- if (isKeyguardOccluded() == isOccluded) {
- return false;
- }
mKeyguardDelegate.setOccluded(isOccluded, notify);
return mKeyguardDelegate.isShowing();
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 94fb840..ca5fa5f 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -67,6 +67,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -91,6 +92,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* This interface supplies all UI-specific behavior of the window manager. An
@@ -357,6 +359,11 @@
* windows even if the rotation hasn't changed.
*/
void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout);
+
+ /**
+ * Invoked when a screenshot is taken of the given display to notify registered listeners.
+ */
+ List<ComponentName> notifyScreenshotListeners(int displayId);
}
/**
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index a82d4ea..5096ad1 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -61,6 +61,7 @@
public final class ShutdownThread extends Thread {
// constants
+ private static final boolean DEBUG = false;
private static final String TAG = "ShutdownThread";
private static final int ACTION_DONE_POLL_WAIT_MS = 500;
private static final int RADIOS_STATE_POLL_SLEEP_MS = 100;
@@ -161,7 +162,9 @@
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
- Log.d(TAG, "Request to shutdown already running, returning.");
+ if (DEBUG) {
+ Log.d(TAG, "Request to shutdown already running, returning.");
+ }
return;
}
}
@@ -178,7 +181,9 @@
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm);
- Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
+ if (DEBUG) {
+ Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
+ }
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
@@ -348,26 +353,34 @@
}
private static boolean showSysuiReboot() {
- Log.d(TAG, "Attempting to use SysUI shutdown UI");
+ if (DEBUG) {
+ Log.d(TAG, "Attempting to use SysUI shutdown UI");
+ }
try {
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
if (service.showShutdownUi(mReboot, mReason)) {
// Sysui will handle shutdown UI.
- Log.d(TAG, "SysUI handling shutdown UI");
+ if (DEBUG) {
+ Log.d(TAG, "SysUI handling shutdown UI");
+ }
return true;
}
} catch (Exception e) {
// If anything went wrong, ignore it and use fallback ui
}
- Log.d(TAG, "SysUI is unavailable");
+ if (DEBUG) {
+ Log.d(TAG, "SysUI is unavailable");
+ }
return false;
}
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
- Log.d(TAG, "Shutdown sequence already running, returning.");
+ if (DEBUG) {
+ Log.d(TAG, "Shutdown sequence already running, returning.");
+ }
return;
}
sIsStarted = true;
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 2fb5cec..bc90c89 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -3925,8 +3925,7 @@
private final HistoryStepDetails mDetails = new HistoryStepDetails();
private boolean mHasHistoryStepDetails;
-
- private int mLastHistoryStepLevel;
+ private boolean mUpdateRequested;
/**
* Total time (in milliseconds) spent executing in user code.
@@ -3956,20 +3955,20 @@
@Override
public HistoryStepDetails getHistoryStepDetails() {
- if (mBatteryLevel >= mLastHistoryStepLevel && mHasHistoryStepDetails) {
- mLastHistoryStepLevel = mBatteryLevel;
- return null;
- }
+ if (!mUpdateRequested) {
+ mUpdateRequested = true;
+ // Perform a CPU update right after we do this collection, so we have started
+ // collecting good data for the next step.
+ requestImmediateCpuUpdate();
- // Perform a CPU update right after we do this collection, so we have started
- // collecting good data for the next step.
- requestImmediateCpuUpdate();
-
- if (mPlatformIdleStateCallback != null) {
- mDetails.statSubsystemPowerState =
- mPlatformIdleStateCallback.getSubsystemLowPowerStats();
- if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
- mDetails.statSubsystemPowerState);
+ if (mPlatformIdleStateCallback != null) {
+ mDetails.statSubsystemPowerState =
+ mPlatformIdleStateCallback.getSubsystemLowPowerStats();
+ if (DEBUG) {
+ Slog.i(TAG,
+ "WRITE SubsystemPowerState:" + mDetails.statSubsystemPowerState);
+ }
+ }
}
if (!mHasHistoryStepDetails) {
@@ -3989,7 +3988,7 @@
mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
- mDetails.clear();
+ return null;
} else {
if (DEBUG) {
Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
@@ -4058,12 +4057,8 @@
mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
+ return mDetails;
}
-
- mHasHistoryStepDetails = mBatteryLevel <= mLastHistoryStepLevel;
- mLastHistoryStepLevel = mBatteryLevel;
-
- return mDetails;
}
public void addCpuStats(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
@@ -4085,6 +4080,11 @@
mCurStepStatIdleTimeMs += statIdleTimeMs;
}
+ public void finishAddingCpuLocked() {
+ mHasHistoryStepDetails = true;
+ mUpdateRequested = false;
+ }
+
@Override
public void clear() {
mHasHistoryStepDetails = false;
@@ -4963,13 +4963,13 @@
}
@GuardedBy("this")
- public boolean startAddingCpuLocked() {
+ public boolean startAddingCpuStatsLocked() {
mExternalSync.cancelCpuSyncDueToWakelockChange();
return mOnBatteryInternal;
}
@GuardedBy("this")
- public void finishAddingCpuLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
+ public void addCpuStatsLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
int statSoftIrqTimeMs, int statIdleTimeMs) {
mStepDetailsCalculator.addCpuStats(totalUTimeMs, totalSTimeMs, statUserTimeMs,
@@ -4977,6 +4977,14 @@
statSoftIrqTimeMs, statIdleTimeMs);
}
+ /**
+ * Called after {@link #addCpuStatsLocked} has been invoked for all active apps.
+ */
+ @GuardedBy("this")
+ public void finishAddingCpuStatsLocked() {
+ mStepDetailsCalculator.finishAddingCpuLocked();
+ }
+
public void noteProcessDiedLocked(int uid, int pid) {
uid = mapUid(uid);
Uid u = mUidStats.get(uid);
@@ -11309,7 +11317,7 @@
*/
@Override
public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
- return mHistory.iterate();
+ return mHistory.copy().iterate();
}
@Override
@@ -14064,7 +14072,8 @@
&& (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|| level >= 90
|| (mDischargeCurrentLevel < 20 && level >= 80)
- || getHighDischargeAmountSinceCharge() >= 200)) {
+ || getHighDischargeAmountSinceCharge() >= 200)
+ && mHistory.isResetEnabled()) {
Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
+ " dischargeLevel=" + mDischargeCurrentLevel
+ " lowAmount=" + getLowDischargeAmountSinceCharge()
@@ -16614,7 +16623,7 @@
}
@GuardedBy("this")
- public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+ public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
if (DEBUG) {
pw.println("mOnBatteryTimeBase:");
mOnBatteryTimeBase.dump(pw, " ");
@@ -16686,36 +16695,39 @@
pr.println("*** Camera timer:");
mCameraOnTimer.logState(pr, " ");
}
- super.dumpLocked(context, pw, flags, reqUid, histStart);
+ super.dump(context, pw, flags, reqUid, histStart);
- pw.print("Per process state tracking available: ");
- pw.println(trackPerProcStateCpuTimes());
- pw.print("Total cpu time reads: ");
- pw.println(mNumSingleUidCpuTimeReads);
- pw.print("Batching Duration (min): ");
- pw.println((mClock.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (60 * 1000));
- pw.print("All UID cpu time reads since the later of device start or stats reset: ");
- pw.println(mNumAllUidCpuTimeReads);
- pw.print("UIDs removed since the later of device start or stats reset: ");
- pw.println(mNumUidsRemoved);
+ synchronized (this) {
+ pw.print("Per process state tracking available: ");
+ pw.println(trackPerProcStateCpuTimes());
+ pw.print("Total cpu time reads: ");
+ pw.println(mNumSingleUidCpuTimeReads);
+ pw.print("Batching Duration (min): ");
+ pw.println((mClock.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (60 * 1000));
+ pw.print("All UID cpu time reads since the later of device start or stats reset: ");
+ pw.println(mNumAllUidCpuTimeReads);
+ pw.print("UIDs removed since the later of device start or stats reset: ");
+ pw.println(mNumUidsRemoved);
- pw.println("Currently mapped isolated uids:");
- final int numIsolatedUids = mIsolatedUids.size();
- for (int i = 0; i < numIsolatedUids; i++) {
- final int isolatedUid = mIsolatedUids.keyAt(i);
- final int ownerUid = mIsolatedUids.valueAt(i);
- final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
- pw.println(" " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
+ pw.println("Currently mapped isolated uids:");
+ final int numIsolatedUids = mIsolatedUids.size();
+ for (int i = 0; i < numIsolatedUids; i++) {
+ final int isolatedUid = mIsolatedUids.keyAt(i);
+ final int ownerUid = mIsolatedUids.valueAt(i);
+ final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
+ pw.println(
+ " " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
+ }
+
+ pw.println();
+ dumpConstantsLocked(pw);
+
+ pw.println();
+ dumpCpuPowerBracketsLocked(pw);
+
+ pw.println();
+ dumpEnergyConsumerStatsLocked(pw);
}
-
- pw.println();
- dumpConstantsLocked(pw);
-
- pw.println();
- dumpCpuPowerBracketsLocked(pw);
-
- pw.println();
- dumpEnergyConsumerStatsLocked(pw);
}
@Override
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 1321873..9d5173a 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -36,7 +36,7 @@
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.hardware.boot.V1_0.IBootControl;
+import android.hardware.boot.IBootControl;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Binder;
@@ -48,6 +48,7 @@
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
@@ -66,6 +67,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.ApexManager;
+import com.android.server.recoverysystem.hal.BootControlHIDL;
import libcore.io.IoUtils;
@@ -155,18 +157,20 @@
/**
* The action to perform upon new resume on reboot prepare request for a given client.
*/
- @IntDef({ ROR_NEED_PREPARATION,
+ @IntDef({ROR_NEED_PREPARATION,
ROR_SKIP_PREPARATION_AND_NOTIFY,
- ROR_SKIP_PREPARATION_NOT_NOTIFY })
- private @interface ResumeOnRebootActionsOnRequest {}
+ ROR_SKIP_PREPARATION_NOT_NOTIFY})
+ private @interface ResumeOnRebootActionsOnRequest {
+ }
/**
* The action to perform upon resume on reboot clear request for a given client.
*/
- @IntDef({ ROR_NOT_REQUESTED,
+ @IntDef({ROR_NOT_REQUESTED,
ROR_REQUESTED_NEED_CLEAR,
- ROR_REQUESTED_SKIP_CLEAR })
- private @interface ResumeOnRebootActionsOnClear {}
+ ROR_REQUESTED_SKIP_CLEAR})
+ private @interface ResumeOnRebootActionsOnClear {
+ }
/**
* Fatal arm escrow errors from lock settings that means the RoR is in a bad state. So clients
@@ -306,19 +310,26 @@
* Throws remote exception if there's an error getting the boot control HAL.
* Returns null if the boot control HAL's version is older than V1_2.
*/
- public android.hardware.boot.V1_2.IBootControl getBootControl() throws RemoteException {
- IBootControl bootControlV10 = IBootControl.getService(true);
- if (bootControlV10 == null) {
- throw new RemoteException("Failed to get boot control HAL V1_0.");
+ public IBootControl getBootControl() throws RemoteException {
+ String serviceName = IBootControl.DESCRIPTOR + "/default";
+ if (ServiceManager.isDeclared(serviceName)) {
+ Slog.i(TAG,
+ "AIDL version of BootControl HAL present, using instance " + serviceName);
+ return IBootControl.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(serviceName));
}
- android.hardware.boot.V1_2.IBootControl bootControlV12 =
- android.hardware.boot.V1_2.IBootControl.castFrom(bootControlV10);
- if (bootControlV12 == null) {
+ IBootControl bootcontrol = BootControlHIDL.getService();
+ if (!BootControlHIDL.isServicePresent()) {
+ Slog.e(TAG, "Neither AIDL nor HIDL version of the BootControl HAL is present.");
+ return null;
+ }
+
+ if (!BootControlHIDL.isV1_2ServicePresent()) {
Slog.w(TAG, "Device doesn't implement boot control HAL V1_2.");
return null;
}
- return bootControlV12;
+ return bootcontrol;
}
public void threadSleep(long millis) throws InterruptedException {
@@ -526,7 +537,7 @@
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.RECOVERY)
!= PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT)
- != PackageManager.PERMISSION_GRANTED) {
+ != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY
+ " or " + android.Manifest.permission.REBOOT + " for resume on reboot.");
}
@@ -738,7 +749,7 @@
return true;
}
- android.hardware.boot.V1_2.IBootControl bootControl;
+ IBootControl bootControl;
try {
bootControl = mInjector.getBootControl();
} catch (RemoteException e) {
@@ -972,8 +983,8 @@
CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile);
if (apexInfoList == null) {
Log.i(TAG, "apex_info.pb not present in OTA package. "
- + "Assuming device doesn't support compressed"
- + "APEX, continueing without allocating space.");
+ + "Assuming device doesn't support compressed"
+ + "APEX, continueing without allocating space.");
return true;
}
ApexManager apexManager = ApexManager.getInstance();
@@ -1160,6 +1171,7 @@
/**
* Reads the status from the uncrypt service which is usually represented as a percentage.
+ *
* @return an integer representing the percentage completed
* @throws IOException if there was an error reading the socket
*/
@@ -1169,6 +1181,7 @@
/**
* Sends a confirmation to the uncrypt service.
+ *
* @throws IOException if there was an error writing to the socket
*/
public void sendAck() throws IOException {
diff --git a/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java b/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
new file mode 100644
index 0000000..65325c2
--- /dev/null
+++ b/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.recoverysystem.hal;
+
+import android.hardware.boot.IBootControl;
+import android.hardware.boot.V1_0.CommandResult;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+public class BootControlHIDL implements IBootControl {
+ private static final String TAG = "BootControlHIDL";
+
+ final android.hardware.boot.V1_0.IBootControl v1_hal;
+ final android.hardware.boot.V1_1.IBootControl v1_1_hal;
+ final android.hardware.boot.V1_2.IBootControl v1_2_hal;
+
+ public static boolean isServicePresent() {
+ try {
+ android.hardware.boot.V1_0.IBootControl.getService(true);
+ } catch (RemoteException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean isV1_2ServicePresent() {
+ try {
+ android.hardware.boot.V1_2.IBootControl.getService(true);
+ } catch (RemoteException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public static BootControlHIDL getService() throws RemoteException {
+ android.hardware.boot.V1_0.IBootControl v1_hal =
+ android.hardware.boot.V1_0.IBootControl.getService(true);
+ android.hardware.boot.V1_1.IBootControl v1_1_hal =
+ android.hardware.boot.V1_1.IBootControl.castFrom(v1_hal);
+ android.hardware.boot.V1_2.IBootControl v1_2_hal =
+ android.hardware.boot.V1_2.IBootControl.castFrom(v1_hal);
+ return new BootControlHIDL(v1_hal, v1_1_hal, v1_2_hal);
+ }
+
+ private BootControlHIDL(android.hardware.boot.V1_0.IBootControl v1_hal,
+ android.hardware.boot.V1_1.IBootControl v1_1_hal,
+ android.hardware.boot.V1_2.IBootControl v1_2_hal) throws RemoteException {
+ this.v1_hal = v1_hal;
+ this.v1_1_hal = v1_1_hal;
+ this.v1_2_hal = v1_2_hal;
+ if (v1_hal == null) {
+ throw new RemoteException("Failed to find V1.0 BootControl HIDL");
+ }
+ if (v1_2_hal != null) {
+ Slog.i(TAG, "V1.2 version of BootControl HIDL HAL available, using V1.2");
+ } else if (v1_1_hal != null) {
+ Slog.i(TAG, "V1.1 version of BootControl HIDL HAL available, using V1.1");
+ } else {
+ Slog.i(TAG, "V1.0 version of BootControl HIDL HAL available, using V1.0");
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
+ @Override
+ public int getActiveBootSlot() throws RemoteException {
+ if (v1_2_hal == null) {
+ throw new RemoteException("getActiveBootSlot() requires V1.2 BootControl HAL");
+ }
+ return v1_2_hal.getActiveBootSlot();
+ }
+
+ @Override
+ public int getCurrentSlot() throws RemoteException {
+ return v1_hal.getCurrentSlot();
+ }
+
+ @Override
+ public int getNumberSlots() throws RemoteException {
+ return v1_hal.getNumberSlots();
+ }
+
+ @Override
+ public int getSnapshotMergeStatus() throws RemoteException {
+ if (v1_1_hal == null) {
+ throw new RemoteException("getSnapshotMergeStatus() requires V1.1 BootControl HAL");
+ }
+ return v1_1_hal.getSnapshotMergeStatus();
+ }
+
+ @Override
+ public String getSuffix(int slot) throws RemoteException {
+ return v1_hal.getSuffix(slot);
+ }
+
+ @Override
+ public boolean isSlotBootable(int slot) throws RemoteException {
+ int ret = v1_hal.isSlotBootable(slot);
+ if (ret == -1) {
+ throw new RemoteException(
+ "isSlotBootable() failed, Slot %d might be invalid.".formatted(slot));
+ }
+ return ret != 0;
+ }
+
+ @Override
+ public boolean isSlotMarkedSuccessful(int slot) throws RemoteException {
+ int ret = v1_hal.isSlotMarkedSuccessful(slot);
+ if (ret == -1) {
+ throw new RemoteException(
+ "isSlotMarkedSuccessful() failed, Slot %d might be invalid.".formatted(slot));
+ }
+ return ret != 0;
+ }
+
+ @Override
+ public void markBootSuccessful() throws RemoteException {
+ CommandResult res = v1_hal.markBootSuccessful();
+ if (!res.success) {
+ throw new RemoteException("Error markBootSuccessful() " + res.errMsg);
+ }
+ }
+
+ @Override
+ public void setActiveBootSlot(int slot) throws RemoteException {
+ CommandResult res = v1_hal.setActiveBootSlot(slot);
+ if (!res.success) {
+ throw new RemoteException("Error setActiveBootSlot(%d) %s".formatted(slot, res.errMsg));
+ }
+ }
+
+ @Override
+ public void setSlotAsUnbootable(int slot) throws RemoteException {
+ CommandResult res = v1_hal.setSlotAsUnbootable(slot);
+ if (!res.success) {
+ throw new RemoteException(
+ "Error setSlotAsUnbootable(%d) %s".formatted(slot, res.errMsg));
+ }
+ }
+
+ @Override
+ public void setSnapshotMergeStatus(int status) throws RemoteException {
+ if (v1_1_hal == null) {
+ throw new RemoteException("getSnapshotMergeStatus() requires V1.1 BootControl HAL");
+ }
+ if (!v1_1_hal.setSnapshotMergeStatus(status)) {
+ throw new RemoteException("Error setSnapshotMergeStatus(%d)".formatted(status));
+ }
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 1;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return v1_hal.interfaceDescriptor();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/OWNERS b/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
deleted file mode 100644
index e5d0370..0000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-ytai@google.com
-elaurent@google.com
diff --git a/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java b/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
index fef7148..ce1d525 100644
--- a/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
+++ b/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
@@ -263,11 +263,11 @@
long gnssUnixEpochTimeMillis = locationTime.getUnixEpochTimeMillis();
long elapsedRealtimeMs = locationTime.getElapsedRealtimeNanos() / 1_000_000L;
- UnixEpochTime timeSignal = new UnixEpochTime(elapsedRealtimeMs, gnssUnixEpochTimeMillis);
- mLastSuggestedGnssTime = timeSignal;
+ UnixEpochTime unixEpochTime = new UnixEpochTime(elapsedRealtimeMs, gnssUnixEpochTimeMillis);
+ mLastSuggestedGnssTime = unixEpochTime;
- GnssTimeSuggestion timeSuggestion = new GnssTimeSuggestion(timeSignal);
- mTimeDetectorInternal.suggestGnssTime(timeSuggestion);
+ GnssTimeSuggestion suggestion = new GnssTimeSuggestion(unixEpochTime);
+ mTimeDetectorInternal.suggestGnssTime(suggestion);
}
@Override
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java
index 24533d7..5df5cbc 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java
@@ -56,11 +56,19 @@
* valid but does not change the time because it matches the current device time is considered
* accepted.
*/
- boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion manualTimeSuggestion);
+ boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion suggestion);
- /** Used to pass new network time suggestions to the time detector. */
- void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal);
+ /**
+ * Suggests a network time to the time detector. The suggestion may not be used by the time
+ * detector to set the device's time depending on device configuration and user settings, but
+ * can replace previous network suggestions received.
+ */
+ void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion);
- /** Used to pass new GNSS time suggestions to the time detector. */
- void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal);
-}
\ No newline at end of file
+ /**
+ * Suggests a GNSS-derived time to the time detector. The suggestion may not be used by the time
+ * detector to set the device's time depending on device configuration and user settings, but
+ * can replace previous GNSS suggestions received.
+ */
+ void suggestGnssTime(@NonNull GnssTimeSuggestion suggestion);
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java
index 9839de0..af168f8 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java
@@ -72,24 +72,24 @@
}
@Override
- public boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion timeSignal) {
- Objects.requireNonNull(timeSignal);
+ public boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion suggestion) {
+ Objects.requireNonNull(suggestion);
int userId = mCurrentUserIdentityInjector.getCurrentUserId();
- return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal, false);
+ return mTimeDetectorStrategy.suggestManualTime(userId, suggestion, false);
}
@Override
- public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) {
- Objects.requireNonNull(timeSignal);
+ public void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion) {
+ Objects.requireNonNull(suggestion);
- mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
+ mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(suggestion));
}
@Override
- public void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) {
- Objects.requireNonNull(timeSignal);
+ public void suggestGnssTime(@NonNull GnssTimeSuggestion suggestion) {
+ Objects.requireNonNull(suggestion);
- mHandler.post(() -> mTimeDetectorStrategy.suggestGnssTime(timeSignal));
+ mHandler.post(() -> mTimeDetectorStrategy.suggestGnssTime(suggestion));
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 3e23953..a9dcff4 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -319,9 +319,9 @@
}
@Override
- public boolean setManualTime(@NonNull ManualTimeSuggestion timeSignal) {
+ public boolean setManualTime(@NonNull ManualTimeSuggestion suggestion) {
enforceManageTimeDetectorPermission();
- Objects.requireNonNull(timeSignal);
+ Objects.requireNonNull(suggestion);
// This calls suggestManualTime() as the logic is identical, it only differs in the
// permission required, which is handled on the line above.
@@ -330,7 +330,7 @@
try {
final boolean bypassUserPolicyChecks = false;
return mTimeDetectorStrategy.suggestManualTime(
- userId, timeSignal, bypassUserPolicyChecks);
+ userId, suggestion, bypassUserPolicyChecks);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -363,11 +363,11 @@
/**
* Suggests network time with permission checks. For use by {@link TimeDetectorShellCommand}.
*/
- void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) {
+ void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion) {
enforceSuggestNetworkTimePermission();
- Objects.requireNonNull(timeSignal);
+ Objects.requireNonNull(suggestion);
- mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
+ mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(suggestion));
}
/**
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 9dca6ec..dbd7172 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -87,7 +87,7 @@
boolean confirmTime(@NonNull UnixEpochTime confirmationTime);
/** Processes the suggested time from telephony sources. */
- void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion);
+ void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion suggestion);
/**
* Processes the suggested manually entered time. Returns {@code false} if the suggestion was
@@ -98,11 +98,15 @@
* @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device
* policy restrictions that should apply to actual users can be ignored
*/
- boolean suggestManualTime(@UserIdInt int userId, @NonNull ManualTimeSuggestion timeSuggestion,
+ boolean suggestManualTime(@UserIdInt int userId, @NonNull ManualTimeSuggestion suggestion,
boolean bypassUserPolicyChecks);
- /** Processes the suggested time from network sources. */
- void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
+ /**
+ * Processes the suggested network time. The suggestion may not be used to set the device's time
+ * depending on device configuration and user settings, but can replace previous network
+ * suggestions received.
+ */
+ void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion);
/**
* Returns the latest (accepted) network time suggestion. Returns {@code null} if there isn't
@@ -119,10 +123,10 @@
void clearLatestNetworkSuggestion();
/** Processes the suggested time from gnss sources. */
- void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion);
+ void suggestGnssTime(@NonNull GnssTimeSuggestion suggestion);
/** Processes the suggested time from external sources. */
- void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion);
+ void suggestExternalTime(@NonNull ExternalTimeSuggestion suggestion);
// Utility methods below are to be moved to a better home when one becomes more obvious.
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 09bb803..d679bbe 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -208,7 +208,7 @@
if (DBG) {
Slog.d(LOG_TAG, "External suggestion received."
+ " currentUserConfig=" + currentUserConfig
- + " newSuggestion=" + suggestion);
+ + " suggestion=" + suggestion);
}
Objects.requireNonNull(suggestion);
@@ -230,7 +230,7 @@
if (DBG) {
Slog.d(LOG_TAG, "GNSS suggestion received."
+ " currentUserConfig=" + currentUserConfig
- + " newSuggestion=" + suggestion);
+ + " suggestion=" + suggestion);
}
Objects.requireNonNull(suggestion);
@@ -289,7 +289,7 @@
if (DBG) {
Slog.d(LOG_TAG, "Network suggestion received."
+ " currentUserConfig=" + currentUserConfig
- + " newSuggestion=" + suggestion);
+ + " suggestion=" + suggestion);
}
Objects.requireNonNull(suggestion);
@@ -311,7 +311,7 @@
// Now perform auto time detection. The new suggestion may be used to modify the system
// clock.
- String reason = "New network time suggested. timeSuggestion=" + suggestion;
+ String reason = "New network time suggested. suggestion=" + suggestion;
doAutoTimeDetection(reason);
}
@@ -396,29 +396,29 @@
}
@Override
- public synchronized void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion) {
+ public synchronized void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion suggestion) {
// Empty time suggestion means that telephony network connectivity has been lost.
// The passage of time is relentless, and we don't expect our users to use a time machine,
// so we can continue relying on previous suggestions when we lose connectivity. This is
// unlike time zone, where a user may lose connectivity when boarding a flight and where we
// do want to "forget" old signals. Suggestions that are too old are discarded later in the
// detection algorithm.
- if (timeSuggestion.getUnixEpochTime() == null) {
+ if (suggestion.getUnixEpochTime() == null) {
return;
}
- if (!validateAutoSuggestionTime(timeSuggestion.getUnixEpochTime(), timeSuggestion)) {
+ if (!validateAutoSuggestionTime(suggestion.getUnixEpochTime(), suggestion)) {
return;
}
// Perform input filtering and record the validated suggestion against the slotIndex.
- if (!storeTelephonySuggestion(timeSuggestion)) {
+ if (!storeTelephonySuggestion(suggestion)) {
return;
}
// Now perform auto time detection. The new suggestion may be used to modify the system
// clock.
- String reason = "New telephony time suggested. timeSuggestion=" + timeSuggestion;
+ String reason = "New telephony time suggested. suggestion=" + suggestion;
doAutoTimeDetection(reason);
}
@@ -623,19 +623,19 @@
+ ", detectionReason=" + detectionReason;
}
} else if (origin == ORIGIN_GNSS) {
- GnssTimeSuggestion gnssTimeSuggestion = findLatestValidGnssSuggestion();
- if (gnssTimeSuggestion != null) {
- newUnixEpochTime = gnssTimeSuggestion.getUnixEpochTime();
+ GnssTimeSuggestion gnssSuggestion = findLatestValidGnssSuggestion();
+ if (gnssSuggestion != null) {
+ newUnixEpochTime = gnssSuggestion.getUnixEpochTime();
cause = "Found good gnss suggestion."
- + ", gnssTimeSuggestion=" + gnssTimeSuggestion
+ + ", gnssSuggestion=" + gnssSuggestion
+ ", detectionReason=" + detectionReason;
}
} else if (origin == ORIGIN_EXTERNAL) {
- ExternalTimeSuggestion externalTimeSuggestion = findLatestValidExternalSuggestion();
- if (externalTimeSuggestion != null) {
- newUnixEpochTime = externalTimeSuggestion.getUnixEpochTime();
+ ExternalTimeSuggestion externalSuggestion = findLatestValidExternalSuggestion();
+ if (externalSuggestion != null) {
+ newUnixEpochTime = externalSuggestion.getUnixEpochTime();
cause = "Found good external suggestion."
- + ", externalTimeSuggestion=" + externalTimeSuggestion
+ + ", externalSuggestion=" + externalSuggestion
+ ", detectionReason=" + detectionReason;
}
} else {
@@ -742,14 +742,14 @@
private static int scoreTelephonySuggestion(
@ElapsedRealtimeLong long elapsedRealtimeMillis,
- @NonNull TelephonyTimeSuggestion timeSuggestion) {
+ @NonNull TelephonyTimeSuggestion suggestion) {
// Validate first.
- UnixEpochTime unixEpochTime = timeSuggestion.getUnixEpochTime();
+ UnixEpochTime unixEpochTime = suggestion.getUnixEpochTime();
if (!validateSuggestionUnixEpochTime(elapsedRealtimeMillis, unixEpochTime)) {
Slog.w(LOG_TAG, "Existing suggestion found to be invalid"
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
- + ", timeSuggestion=" + timeSuggestion);
+ + ", suggestion=" + suggestion);
return TELEPHONY_INVALID_SCORE;
}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 5cb48c2..449b41a 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -18,6 +18,7 @@
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
+import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -27,6 +28,7 @@
import com.android.server.SystemTimeZone.TimeZoneConfidence;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
@@ -35,7 +37,10 @@
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
- EnvironmentImpl() {
+ @NonNull private final Handler mHandler;
+
+ EnvironmentImpl(@NonNull Handler handler) {
+ mHandler = Objects.requireNonNull(handler);
}
@Override
@@ -72,4 +77,9 @@
public void dumpDebugLog(@NonNull PrintWriter printWriter) {
SystemTimeZone.dump(printWriter);
}
+
+ @Override
+ public void runAsync(@NonNull Runnable runnable) {
+ mHandler.post(runnable);
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index 74a518b..6c0ce0c 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -56,7 +56,7 @@
* valid but does not change the time zone because it matches the current device time zone is
* considered accepted.
*/
- boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion);
+ boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion suggestion);
/**
* Handles the supplied {@link LocationAlgorithmEvent}. The detector may ignore the event based
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 07d0473..fad27f5 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -66,13 +66,13 @@
}
@Override
- public boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
- Objects.requireNonNull(timeZoneSuggestion);
+ public boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion suggestion) {
+ Objects.requireNonNull(suggestion);
int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId();
final boolean bypassUserPolicyChecks = true;
return mTimeZoneDetectorStrategy.suggestManualTimeZone(
- currentUserId, timeZoneSuggestion, bypassUserPolicyChecks);
+ currentUserId, suggestion, bypassUserPolicyChecks);
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 10cd5d1..dac4bf8 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -346,7 +346,7 @@
}
@Override
- public boolean setManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+ public boolean setManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
enforceManageTimeZoneDetectorPermission();
// This calls suggestManualTimeZone() as the logic is identical, it only differs in the
@@ -356,34 +356,34 @@
try {
final boolean bypassUserPolicyChecks = false;
return mTimeZoneDetectorStrategy.suggestManualTimeZone(
- userId, timeZoneSuggestion, bypassUserPolicyChecks);
+ userId, suggestion, bypassUserPolicyChecks);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
@Override
- public boolean suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+ public boolean suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
enforceSuggestManualTimeZonePermission();
- Objects.requireNonNull(timeZoneSuggestion);
+ Objects.requireNonNull(suggestion);
int userId = mCallerIdentityInjector.getCallingUserId();
final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
final boolean bypassUserPolicyChecks = false;
return mTimeZoneDetectorStrategy.suggestManualTimeZone(
- userId, timeZoneSuggestion, bypassUserPolicyChecks);
+ userId, suggestion, bypassUserPolicyChecks);
} finally {
mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
@Override
- public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+ public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion) {
enforceSuggestTelephonyTimeZonePermission();
- Objects.requireNonNull(timeZoneSuggestion);
+ Objects.requireNonNull(suggestion);
- mHandler.post(() -> mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion));
+ mHandler.post(() -> mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(suggestion));
}
boolean isTelephonyTimeZoneDetectionSupported() {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index e0e3565..dddb46f 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -102,6 +102,11 @@
* Dumps the time zone debug log to the supplied {@link PrintWriter}.
*/
void dumpDebugLog(PrintWriter printWriter);
+
+ /**
+ * Requests that the supplied runnable be invoked asynchronously.
+ */
+ void runAsync(@NonNull Runnable runnable);
}
private static final String LOG_TAG = TimeZoneDetectorService.TAG;
@@ -200,10 +205,6 @@
@NonNull
private final ServiceConfigAccessor mServiceConfigAccessor;
- /** The handler used for asynchronous operations triggered by this. */
- @NonNull
- private final Handler mStateChangeHandler;
-
@GuardedBy("this")
@NonNull private final List<StateChangeListener> mStateChangeListeners = new ArrayList<>();
@@ -246,17 +247,16 @@
public static TimeZoneDetectorStrategyImpl create(
@NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) {
- Environment environment = new EnvironmentImpl();
- return new TimeZoneDetectorStrategyImpl(serviceConfigAccessor, handler, environment);
+ Environment environment = new EnvironmentImpl(handler);
+ return new TimeZoneDetectorStrategyImpl(serviceConfigAccessor, environment);
}
@VisibleForTesting
public TimeZoneDetectorStrategyImpl(
@NonNull ServiceConfigAccessor serviceConfigAccessor,
- @NonNull Handler handler, @NonNull Environment environment) {
+ @NonNull Environment environment) {
mEnvironment = Objects.requireNonNull(environment);
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
- mStateChangeHandler = Objects.requireNonNull(handler);
// Start with telephony fallback enabled.
mTelephonyTimeZoneFallbackEnabled =
@@ -349,7 +349,7 @@
private void notifyStateChangeListenersAsynchronously() {
for (StateChangeListener listener : mStateChangeListeners) {
// This is queuing asynchronous notification, so no need to surrender the "this" lock.
- mStateChangeHandler.post(listener::onChange);
+ mEnvironment.runAsync(listener::onChange);
}
}
@@ -479,7 +479,7 @@
ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
if (DBG) {
Slog.d(LOG_TAG, "Telephony suggestion received. currentUserConfig=" + currentUserConfig
- + " newSuggestion=" + suggestion);
+ + " suggestion=" + suggestion);
}
Objects.requireNonNull(suggestion);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
old mode 100755
new mode 100644
index 73b2238..0928bef
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -43,6 +44,7 @@
import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.AdBuffer;
import android.media.tv.AdRequest;
@@ -91,7 +93,6 @@
import android.util.SparseArray;
import android.view.InputChannel;
import android.view.Surface;
-
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -102,9 +103,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.android.server.SystemService;
-
import dalvik.annotation.optimization.NeverCompile;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -814,7 +813,7 @@
@GuardedBy("mLock")
private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
- int userId) {
+ int userId, AttributionSource tvAppAttributionSource) {
UserState userState = getOrCreateUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (DEBUG) {
@@ -833,8 +832,8 @@
service.createRecordingSession(
callback, sessionState.inputId, sessionState.sessionId);
} else {
- service.createSession(
- channels[1], callback, sessionState.inputId, sessionState.sessionId);
+ service.createSession(channels[1], callback, sessionState.inputId,
+ sessionState.sessionId, tvAppAttributionSource);
}
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
@@ -1486,7 +1485,8 @@
@Override
public void createSession(final ITvInputClient client, final String inputId,
- boolean isRecordingSession, int seq, int userId) {
+ AttributionSource tvAppAttributionSource, boolean isRecordingSession, int seq,
+ int userId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
@@ -1557,7 +1557,7 @@
if (serviceState.service != null) {
if (!createSessionInternalLocked(serviceState.service, sessionToken,
- resolvedUserId)) {
+ resolvedUserId, tvAppAttributionSource)) {
removeSessionStateLocked(sessionToken, resolvedUserId);
}
} else {
@@ -1836,6 +1836,28 @@
}
@Override
+ public void selectAudioPresentation(IBinder sessionToken, int presentationId,
+ int programId, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "selectAudioPresentation");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid,
+ resolvedUserId).selectAudioPresentation(
+ presentationId, programId);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in selectAudioPresentation", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -2184,10 +2206,10 @@
return null;
}
- final long identity = Binder.clearCallingIdentity();
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "acquireTvInputHardware");
+ final long identity = Binder.clearCallingIdentity();
try {
return mTvInputHardwareManager.acquireHardware(
deviceId, callback, info, callingUid, resolvedUserId,
@@ -2205,10 +2227,10 @@
return;
}
- final long identity = Binder.clearCallingIdentity();
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "releaseTvInputHardware");
+ final long identity = Binder.clearCallingIdentity();
try {
mTvInputHardwareManager.releaseHardware(
deviceId, hardware, callingUid, resolvedUserId);
@@ -2350,10 +2372,10 @@
throws RemoteException {
ensureCaptureTvInputPermission();
- final long identity = Binder.clearCallingIdentity();
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "getAvailableTvStreamConfigList");
+ final long identity = Binder.clearCallingIdentity();
try {
return mTvInputHardwareManager.getAvailableTvStreamConfigList(
inputId, callingUid, resolvedUserId);
@@ -2368,10 +2390,10 @@
throws RemoteException {
ensureCaptureTvInputPermission();
- final long identity = Binder.clearCallingIdentity();
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "captureFrame");
+ final long identity = Binder.clearCallingIdentity();
try {
String hardwareInputId = null;
synchronized (mLock) {
@@ -2400,10 +2422,10 @@
@Override
public boolean isSingleSessionActive(int userId) throws RemoteException {
ensureCaptureTvInputPermission();
- final long identity = Binder.clearCallingIdentity();
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "isSingleSessionActive");
+ final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
@@ -3137,7 +3159,8 @@
// And create sessions, if any.
for (IBinder sessionToken : serviceState.sessionTokens) {
- if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
+ if (!createSessionInternalLocked(
+ serviceState.service, sessionToken, mUserId, null)) {
tokensToBeRemoved.add(sessionToken);
}
}
@@ -3360,6 +3383,43 @@
}
@Override
+ public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAudioPresentationChanged(" + audioPresentations + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAudioPresentationsChanged(audioPresentations,
+ mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAudioPresentationsChanged", e);
+ }
+ }
+ }
+
+ @Override
+ public void onAudioPresentationSelected(int presentationId, int programId) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAudioPresentationSelected(presentationId=" + presentationId
+ + ", programId=" + programId + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAudioPresentationSelected(presentationId, programId,
+ mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAudioPresentationSelected", e);
+ }
+ }
+ }
+
+ @Override
public void onTracksChanged(List<TvTrackInfo> tracks) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java
index 25ce280..625e7d9 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperData.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java
@@ -56,6 +56,12 @@
int mWhich;
/**
+ * True if the system wallpaper was also used for lock screen before this wallpaper was set.
+ * This is needed to update state after setting the wallpaper.
+ */
+ boolean mSystemWasBoth;
+
+ /**
* Callback once the set + crop is finished
*/
IWallpaperManagerCallback setComplete;
@@ -139,6 +145,61 @@
(wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP);
}
+ /**
+ * Copies the essential properties of a WallpaperData to a new instance, including the id and
+ * WallpaperConnection, usually in preparation for migrating a system+lock wallpaper to system-
+ * or lock-only. NB: the source object retains the pointer to the connection and it is the
+ * caller's responsibility to set this to null or otherwise be sure the connection is not shared
+ * between WallpaperData instances.
+ *
+ * @param source WallpaperData object to copy
+ */
+ WallpaperData(WallpaperData source) {
+ this.userId = source.userId;
+ this.wallpaperFile = source.wallpaperFile;
+ this.cropFile = source.cropFile;
+ this.wallpaperComponent = source.wallpaperComponent;
+ this.mWhich = source.mWhich;
+ this.wallpaperId = source.wallpaperId;
+ this.cropHint.set(source.cropHint);
+ this.allowBackup = source.allowBackup;
+ this.primaryColors = source.primaryColors;
+ this.mWallpaperDimAmount = source.mWallpaperDimAmount;
+ this.connection = source.connection;
+ this.connection.mWallpaper = this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder(defaultString(this));
+ out.append(", id: ");
+ out.append(wallpaperId);
+ out.append(", which: ");
+ out.append(mWhich);
+ out.append(", file mod: ");
+ out.append(wallpaperFile != null ? wallpaperFile.lastModified() : "null");
+ if (connection == null) {
+ out.append(", no connection");
+ } else {
+ out.append(", info: ");
+ out.append(connection.mInfo);
+ out.append(", engine(s):");
+ connection.forEachDisplayConnector(connector -> {
+ if (connector.mEngine != null) {
+ out.append(" ");
+ out.append(defaultString(connector.mEngine));
+ } else {
+ out.append(" null");
+ }
+ });
+ }
+ return out.toString();
+ }
+
+ private static String defaultString(Object o) {
+ return o.getClass().getSimpleName() + "@" + Integer.toHexString(o.hashCode());
+ }
+
// Called during initialization of a given user's wallpaper bookkeeping
boolean cropExists() {
return cropFile.exists();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index bf09b67..262b964 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -194,7 +194,11 @@
}
private final Object mLock = new Object();
- private final boolean mEnableSeparateLockScreenEngine;
+ /** True to enable a second engine for lock screen wallpaper when different from system wp. */
+ @VisibleForTesting
+ final boolean mEnableSeparateLockScreenEngine;
+ /** Tracks wallpaper being migrated from system+lock to lock when setting static wp. */
+ WallpaperDestinationChangeHandler mPendingMigrationViaStatic;
/**
* Minimum time between crashes of a wallpaper service for us to consider
@@ -241,11 +245,161 @@
return (wallpaper != null) ? wallpaper : mWallpaper;
}
- @Override
- public void onEvent(int event, String path) {
- if (path == null) {
+ // Handles static wallpaper changes generated by WallpaperObserver events when
+ // mEnableSeparateLockScreenEngine is true.
+ private void updateWallpapers(int event, String path) {
+ // System and system+lock changes happen on the system wallpaper input file;
+ // lock-only changes happen on the dedicated lock wallpaper input file
+ final File changedFile = new File(mWallpaperDir, path);
+ final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
+ final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
+ final WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
+
+ final boolean moved = (event == MOVED_TO);
+ final boolean written = (event == CLOSE_WRITE || moved);
+ final boolean isMigration = moved && lockWallpaperChanged;
+ final boolean isRestore = moved && !isMigration;
+ final boolean isAppliedToLock = (wallpaper.mWhich & FLAG_LOCK) != 0;
+ final boolean needsUpdate = wallpaper.wallpaperComponent == null
+ || event != CLOSE_WRITE // includes the MOVED_TO case
+ || wallpaper.imageWallpaperPending;
+
+ if (DEBUG) {
+ Slog.v(TAG, "Wallpaper file change: evt=" + event
+ + " path=" + path
+ + " sys=" + sysWallpaperChanged
+ + " lock=" + lockWallpaperChanged
+ + " imagePending=" + wallpaper.imageWallpaperPending
+ + " mWhich=0x" + Integer.toHexString(wallpaper.mWhich)
+ + " written=" + written
+ + " isMigration=" + isMigration
+ + " isRestore=" + isRestore
+ + " isAppliedToLock=" + isAppliedToLock
+ + " needsUpdate=" + needsUpdate);
+ }
+
+ if (isMigration) {
+ // When separate lock screen engine is supported, migration will be handled by
+ // WallpaperDestinationChangeHandler.
return;
}
+ if (!(sysWallpaperChanged || lockWallpaperChanged)) {
+ return;
+ }
+
+ int notifyColorsWhich = 0;
+ synchronized (mLock) {
+ notifyCallbacksLocked(wallpaper);
+
+ if (!written || !needsUpdate) {
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Setting new static wallpaper: which=" + wallpaper.mWhich);
+ }
+
+ WallpaperDestinationChangeHandler localSync = mPendingMigrationViaStatic;
+ mPendingMigrationViaStatic = null;
+ // The image source has finished writing the source image,
+ // so we now produce the crop rect (in the background), and
+ // only publish the new displayable (sub)image as a result
+ // of that work.
+ SELinux.restorecon(changedFile);
+ if (isRestore) {
+ // This is a restore, so generate the crop using any just-restored new
+ // crop guidelines, making sure to preserve our local dimension hints.
+ // We also make sure to reapply the correct SELinux label.
+ if (DEBUG) {
+ Slog.v(TAG, "Wallpaper restore; reloading metadata");
+ }
+ loadSettingsLocked(wallpaper.userId, true);
+ }
+ if (DEBUG) {
+ Slog.v(TAG, "Wallpaper written; generating crop");
+ }
+ mWallpaperCropper.generateCrop(wallpaper);
+ if (DEBUG) {
+ Slog.v(TAG, "Crop done; invoking completion callback");
+ }
+ wallpaper.imageWallpaperPending = false;
+
+ if (sysWallpaperChanged) {
+ if (DEBUG) {
+ Slog.v(TAG, "Home screen wallpaper changed");
+ }
+ IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "publish system wallpaper changed!");
+ }
+ if (localSync != null) {
+ localSync.complete();
+ }
+ notifyWallpaperChanged(wallpaper);
+ }
+ };
+
+ // If this was the system wallpaper, rebind...
+ bindWallpaperComponentLocked(mImageWallpaper, true, false, wallpaper,
+ callback);
+ notifyColorsWhich |= FLAG_SYSTEM;
+ }
+
+ if (lockWallpaperChanged) {
+ // This is lock-only, so (re)bind to the static engine.
+ if (DEBUG) {
+ Slog.v(TAG, "Lock screen wallpaper changed");
+ }
+ IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "publish lock wallpaper changed!");
+ }
+ if (localSync != null) {
+ localSync.complete();
+ }
+ notifyWallpaperChanged(wallpaper);
+ }
+ };
+
+ bindWallpaperComponentLocked(mImageWallpaper, true /* force */,
+ false /* fromUser */, wallpaper, callback);
+ notifyColorsWhich |= FLAG_LOCK;
+ } else if (isAppliedToLock) {
+ // This is system-plus-lock: we need to wipe the lock bookkeeping since
+ // we're falling back to displaying the system wallpaper there.
+ if (DEBUG) {
+ Slog.v(TAG, "Lock screen wallpaper changed to same as home");
+ }
+ final WallpaperData lockedWallpaper = mLockWallpaperMap.get(
+ mWallpaper.userId);
+ if (lockedWallpaper != null) {
+ detachWallpaperLocked(lockedWallpaper);
+ }
+ mLockWallpaperMap.remove(wallpaper.userId);
+ notifyColorsWhich |= FLAG_LOCK;
+ }
+
+ saveSettingsLocked(wallpaper.userId);
+ // Notify the client immediately if only lockscreen wallpaper changed.
+ if (lockWallpaperChanged && !sysWallpaperChanged) {
+ notifyWallpaperChanged(wallpaper);
+ }
+ }
+
+ // Outside of the lock since it will synchronize itself
+ if (notifyColorsWhich != 0) {
+ notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich);
+ }
+ }
+
+ // Handles static wallpaper changes generated by WallpaperObserver events when
+ // mEnableSeparateLockScreenEngine is false.
+ // TODO(b/266818039) Remove this method
+ private void updateWallpapersLegacy(int event, String path) {
final boolean moved = (event == MOVED_TO);
final boolean written = (event == CLOSE_WRITE || moved);
final File changedFile = new File(mWallpaperDir, path);
@@ -268,7 +422,6 @@
}
if (moved && lockWallpaperChanged) {
- // TODO(b/253507223) Start lock screen WallpaperService
// We just migrated sys -> lock to preserve imagery for an impending
// new system-only wallpaper. Tell keyguard about it and make sure it
// has the right SELinux label.
@@ -323,8 +476,6 @@
false, wallpaper, callback);
notifyColorsWhich |= FLAG_SYSTEM;
}
- // TODO(b/253507223) Start lock screen WallpaperService if only lock
- // screen wp changed
if (lockWallpaperChanged
|| (wallpaper.mWhich & FLAG_LOCK) != 0) {
if (DEBUG) {
@@ -356,6 +507,19 @@
notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich);
}
}
+
+ @Override
+ public void onEvent(int event, String path) {
+ if (path == null) {
+ return;
+ }
+
+ if (mEnableSeparateLockScreenEngine) {
+ updateWallpapers(event, path);
+ } else {
+ updateWallpapersLegacy(event, path);
+ }
+ }
}
private void notifyWallpaperChanged(WallpaperData wallpaper) {
@@ -382,6 +546,9 @@
}
void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying wallpaper colors changed");
+ }
if (wallpaper.connection != null) {
wallpaper.connection.forEachDisplayConnector(connector -> {
notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId);
@@ -807,7 +974,7 @@
* A map for each display.
* Use {@link #getDisplayConnectorOrCreate(int displayId)} to ensure the display is usable.
*/
- private SparseArray<DisplayConnector> mDisplayConnector = new SparseArray<>();
+ private final SparseArray<DisplayConnector> mDisplayConnector = new SparseArray<>();
/** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
* middle of an update). If exceeded, the wallpaper gets reset to the system default. */
@@ -1170,6 +1337,110 @@
}
}
+ /**
+ * Tracks wallpaper information during a wallpaper change and does bookkeeping afterwards to
+ * update Engine destination, wallpaper maps, and last wallpaper.
+ */
+ class WallpaperDestinationChangeHandler {
+ final WallpaperData mNewWallpaper;
+ final WallpaperData mOriginalSystem;
+
+ WallpaperDestinationChangeHandler(WallpaperData newWallpaper) {
+ this.mNewWallpaper = newWallpaper;
+ WallpaperData sysWp = mWallpaperMap.get(newWallpaper.userId);
+ mOriginalSystem = new WallpaperData(sysWp);
+ }
+
+ void complete() {
+ // Only changes from home+lock to just home or lock need attention
+ // If setting the wallpaper fails, this callback will be called
+ // when the wallpaper is detached, in which case wallpapers may have
+ // already changed. Make sure we're not overwriting a more recent wallpaper.
+ if (mNewWallpaper.mSystemWasBoth) {
+ if (DEBUG) {
+ Slog.v(TAG, "Handling change from system+lock wallpaper");
+ }
+ if (mNewWallpaper.mWhich == FLAG_SYSTEM) {
+ // New wp is system only, so old system+lock is now lock only
+ final boolean originalIsStatic = mImageWallpaper.equals(
+ mOriginalSystem.wallpaperComponent);
+ if (originalIsStatic) {
+ // Static wp: image file rename has already been tried via
+ // migrateStaticSystemToLockWallpaperLocked() and added to the lock wp map
+ // if successful.
+ WallpaperData lockWp = mLockWallpaperMap.get(mNewWallpaper.userId);
+ if (lockWp != null) {
+ // Successful rename, set old system+lock to the pending lock wp
+ if (DEBUG) {
+ Slog.v(TAG, "static system+lock to system success");
+ }
+ lockWp.wallpaperComponent =
+ mOriginalSystem.wallpaperComponent;
+ lockWp.connection = mOriginalSystem.connection;
+ lockWp.connection.mWallpaper = lockWp;
+ updateEngineFlags(mOriginalSystem, FLAG_LOCK);
+ notifyWallpaperColorsChanged(lockWp, FLAG_LOCK);
+ } else {
+ // Failed rename, use current system wp for both
+ if (DEBUG) {
+ Slog.v(TAG, "static system+lock to system failure");
+ }
+ WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
+ currentSystem.mWhich = FLAG_SYSTEM | FLAG_LOCK;
+ updateEngineFlags(currentSystem, FLAG_SYSTEM | FLAG_LOCK);
+ mLockWallpaperMap.remove(mNewWallpaper.userId);
+ }
+ } else {
+ // Live wp: just update old system+lock to lock only
+ if (DEBUG) {
+ Slog.v(TAG, "live system+lock to system success");
+ }
+ mOriginalSystem.mWhich = FLAG_LOCK;
+ updateEngineFlags(mOriginalSystem, FLAG_LOCK);
+ mLockWallpaperMap.put(mNewWallpaper.userId, mOriginalSystem);
+ mLastLockWallpaper = mOriginalSystem;
+ notifyWallpaperColorsChanged(mOriginalSystem, FLAG_LOCK);
+ }
+ } else if (mNewWallpaper.mWhich == FLAG_LOCK) {
+ // New wp is lock only, so old system+lock is now system only
+ if (DEBUG) {
+ Slog.v(TAG, "system+lock to lock");
+ }
+ WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
+ if (currentSystem.wallpaperId == mOriginalSystem.wallpaperId) {
+ currentSystem.mWhich = FLAG_SYSTEM;
+ updateEngineFlags(currentSystem, FLAG_SYSTEM);
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "--- wallpaper changed --");
+ Slog.v(TAG, "new sysWp: " + mWallpaperMap.get(mCurrentUserId));
+ Slog.v(TAG, "new lockWp: " + mLockWallpaperMap.get(mCurrentUserId));
+ Slog.v(TAG, "new lastWp: " + mLastWallpaper);
+ Slog.v(TAG, "new lastLockWp: " + mLastLockWallpaper);
+ }
+ }
+
+ private void updateEngineFlags(WallpaperData wallpaper, @SetWallpaperFlags int which) {
+ if (wallpaper.connection == null) {
+ return;
+ }
+ wallpaper.connection.forEachDisplayConnector(
+ connector -> {
+ try {
+ if (connector.mEngine != null) {
+ connector.mEngine.setWallpaperFlags(which);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to update wallpaper engine flags", e);
+ }
+ }
+ );
+ }
+ }
+
class MyPackageMonitor extends PackageMonitor {
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
@@ -1345,6 +1616,9 @@
mEnableSeparateLockScreenEngine = mContext.getResources().getBoolean(
R.bool.config_independentLockscreenLiveWallpaper);
+ if (DEBUG) {
+ Slog.v(TAG, "Separate lock screen engine enabled: " + mEnableSeparateLockScreenEngine);
+ }
LocalServices.addService(WallpaperManagerInternal.class, new LocalService());
}
@@ -2467,23 +2741,33 @@
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
WallpaperData wallpaper;
+ final WallpaperData originalSystemWallpaper = mWallpaperMap.get(userId);
+ final boolean systemIsStatic =
+ originalSystemWallpaper != null && mImageWallpaper.equals(
+ originalSystemWallpaper.wallpaperComponent);
+ final boolean systemIsBoth = mLockWallpaperMap.get(userId) == null;
/* If we're setting system but not lock, and lock is currently sharing the system
* wallpaper, we need to migrate that image over to being lock-only before
* the caller here writes new bitmap data.
*/
- if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
+ if (which == FLAG_SYSTEM && systemIsStatic && systemIsBoth) {
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
- + "updating system wallpaper");
- migrateSystemToLockWallpaperLocked(userId);
+ + " updating system wallpaper");
+ migrateStaticSystemToLockWallpaperLocked(userId);
}
wallpaper = getWallpaperSafeLocked(userId, which);
+ if (mPendingMigrationViaStatic != null) {
+ Slog.w(TAG, "Starting new static wp migration before previous migration finished");
+ }
+ mPendingMigrationViaStatic = new WallpaperDestinationChangeHandler(wallpaper);
final long ident = Binder.clearCallingIdentity();
try {
ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
if (pfd != null) {
wallpaper.imageWallpaperPending = true;
+ wallpaper.mSystemWasBoth = systemIsBoth;
wallpaper.mWhich = which;
wallpaper.setComplete = completion;
wallpaper.fromForegroundApp = fromForegroundApp;
@@ -2498,7 +2782,7 @@
}
}
- private void migrateSystemToLockWallpaperLocked(int userId) {
+ private void migrateStaticSystemToLockWallpaperLocked(int userId) {
WallpaperData sysWP = mWallpaperMap.get(userId);
if (sysWP == null) {
if (DEBUG) {
@@ -2520,14 +2804,17 @@
try {
Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
+ mLockWallpaperMap.put(userId, lockWP);
+ if (mEnableSeparateLockScreenEngine) {
+ SELinux.restorecon(lockWP.wallpaperFile);
+ mLastLockWallpaper = lockWP;
+ }
} catch (ErrnoException e) {
Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
lockWP.wallpaperFile.delete();
lockWP.cropFile.delete();
return;
}
-
- mLockWallpaperMap.put(userId, lockWP);
}
ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
@@ -2582,11 +2869,116 @@
@VisibleForTesting
void setWallpaperComponent(ComponentName name, @SetWallpaperFlags int which, int userId) {
+ if (mEnableSeparateLockScreenEngine) {
+ setWallpaperComponentInternal(name, which, userId);
+ } else {
+ setWallpaperComponentInternalLegacy(name, which, userId);
+ }
+ }
+
+ private void setWallpaperComponentInternal(ComponentName name, @SetWallpaperFlags int which,
+ int userIdIn) {
+ if (DEBUG) {
+ Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name);
+ }
+ final int userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(),
+ userIdIn, false /* all */, true /* full */, "changing live wallpaper",
+ null /* pkg */);
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
+
+ boolean shouldNotifyColors = false;
+ final WallpaperData newWallpaper;
+
+ synchronized (mLock) {
+ Slog.v(TAG, "setWallpaperComponent name=" + name);
+ final WallpaperData originalSystemWallpaper = mWallpaperMap.get(userId);
+ if (originalSystemWallpaper == null) {
+ throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+ }
+ final boolean systemIsStatic = mImageWallpaper.equals(
+ originalSystemWallpaper.wallpaperComponent);
+ final boolean systemIsBoth = mLockWallpaperMap.get(userId) == null;
+
+ if (which == FLAG_SYSTEM && systemIsBoth && systemIsStatic) {
+ // Migrate current static system+lock wp to lock only before proceeding.
+ Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ + "updating system wallpaper");
+ migrateStaticSystemToLockWallpaperLocked(userId);
+ }
+
+ newWallpaper = getWallpaperSafeLocked(userId, which);
+ final long ident = Binder.clearCallingIdentity();
+
+ try {
+ newWallpaper.imageWallpaperPending = false;
+ newWallpaper.mWhich = which;
+ newWallpaper.mSystemWasBoth = systemIsBoth;
+ final WallpaperDestinationChangeHandler
+ liveSync = new WallpaperDestinationChangeHandler(
+ newWallpaper);
+ boolean same = changingToSame(name, newWallpaper);
+ IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "publish system wallpaper changed!");
+ }
+ liveSync.complete();
+ }
+ };
+ boolean bindSuccess = bindWallpaperComponentLocked(name, /* force */
+ false, /* fromUser */ true, newWallpaper, callback);
+ if (bindSuccess) {
+ if (!same) {
+ newWallpaper.primaryColors = null;
+ } else {
+ if (newWallpaper.connection != null) {
+ newWallpaper.connection.forEachDisplayConnector(displayConnector -> {
+ try {
+ if (displayConnector.mEngine != null) {
+ displayConnector.mEngine.dispatchWallpaperCommand(
+ COMMAND_REAPPLY, 0, 0, 0, null);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error sending apply message to wallpaper", e);
+ }
+ });
+ }
+ }
+ newWallpaper.wallpaperId = makeWallpaperIdLocked();
+ notifyCallbacksLocked(newWallpaper);
+ shouldNotifyColors = true;
+
+ if (which == (FLAG_SYSTEM | FLAG_LOCK)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Lock screen wallpaper changed to same as home");
+ }
+ final WallpaperData lockedWallpaper = mLockWallpaperMap.get(
+ newWallpaper.userId);
+ if (lockedWallpaper != null) {
+ detachWallpaperLocked(lockedWallpaper);
+ }
+ mLockWallpaperMap.remove(newWallpaper.userId);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ if (shouldNotifyColors) {
+ notifyWallpaperColorsChanged(newWallpaper, which);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
+ }
+ }
+
+ // TODO(b/266818039) Remove this method
+ private void setWallpaperComponentInternalLegacy(ComponentName name,
+ @SetWallpaperFlags int which, int userId) {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
- // TODO(b/253507223) Use passed destination and properly start lock screen LWP
int legacyWhich = FLAG_SYSTEM;
boolean shouldNotifyColors = false;
WallpaperData wallpaper;
@@ -2609,7 +3001,7 @@
// therefore it's a shared system+lock image that we need to migrate.
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ "updating system wallpaper");
- migrateSystemToLockWallpaperLocked(userId);
+ migrateStaticSystemToLockWallpaperLocked(userId);
}
}
@@ -2689,8 +3081,6 @@
if (componentName == null) {
// Fall back to static image wallpaper
componentName = mImageWallpaper;
- //clearWallpaperComponentLocked();
- //return;
if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
}
}
@@ -2713,11 +3103,13 @@
return false;
}
+ // This will only get set for non-static wallpapers.
WallpaperInfo wi = null;
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
if (componentName != null && !componentName.equals(mImageWallpaper)) {
- // Make sure the selected service is actually a wallpaper service.
+ // The requested component is not the static wallpaper service, so make sure it's
+ // actually a wallpaper service.
List<ResolveInfo> ris =
mIPackageManager.queryIntentServices(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
@@ -2789,13 +3181,13 @@
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.wallpaper_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, clientIntent);
- if (!mContext.bindServiceAsUser(intent, newConn,
+ boolean bindSuccess = mContext.bindServiceAsUser(intent, newConn,
Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_INCLUDE_CAPABILITIES,
- new UserHandle(serviceUserId))) {
- String msg = "Unable to bind service: "
- + componentName;
+ new UserHandle(serviceUserId));
+ if (!bindSuccess) {
+ String msg = "Unable to bind service: " + componentName;
if (fromUser) {
throw new IllegalArgumentException(msg);
}
@@ -2834,15 +3226,15 @@
// Updates tracking of the currently bound wallpapers. Assumes mEnableSeparateLockScreenEngine
// is true.
private void updateCurrentWallpapers(WallpaperData newWallpaper) {
- if (newWallpaper.userId == mCurrentUserId && !newWallpaper.equals(mFallbackWallpaper)) {
- if (newWallpaper.mWhich == (FLAG_SYSTEM | FLAG_LOCK)) {
- mLastWallpaper = newWallpaper;
- mLastLockWallpaper = null;
- } else if (newWallpaper.mWhich == FLAG_SYSTEM) {
- mLastWallpaper = newWallpaper;
- } else if (newWallpaper.mWhich == FLAG_LOCK) {
- mLastLockWallpaper = newWallpaper;
- }
+ if (newWallpaper.userId != mCurrentUserId || newWallpaper.equals(mFallbackWallpaper)) {
+ return;
+ }
+ if (newWallpaper.mWhich == (FLAG_SYSTEM | FLAG_LOCK)) {
+ mLastWallpaper = newWallpaper;
+ } else if (newWallpaper.mWhich == FLAG_SYSTEM) {
+ mLastWallpaper = newWallpaper;
+ } else if (newWallpaper.mWhich == FLAG_LOCK) {
+ mLastLockWallpaper = newWallpaper;
}
}
@@ -2854,10 +3246,8 @@
}
boolean homeUpdated = (newWallpaper.mWhich & FLAG_SYSTEM) != 0;
boolean lockUpdated = (newWallpaper.mWhich & FLAG_LOCK) != 0;
- // This is the case where a home+lock wallpaper was changed to home-only, and the old
- // home+lock became (static) or will become (live) lock-only.
- boolean lockNeedsHomeWallpaper = mLastLockWallpaper == null && !lockUpdated;
- if (mLastWallpaper != null && homeUpdated && !lockNeedsHomeWallpaper) {
+ boolean systemWillBecomeLock = newWallpaper.mSystemWasBoth && !lockUpdated;
+ if (mLastWallpaper != null && homeUpdated && !systemWillBecomeLock) {
detachWallpaperLocked(mLastWallpaper);
}
if (mLastLockWallpaper != null && lockUpdated) {
@@ -2865,8 +3255,13 @@
}
}
+ // Frees up all rendering resources used by the given wallpaper so that the WallpaperData object
+ // can be reused: detaches Engine, unbinds WallpaperService, etc.
private void detachWallpaperLocked(WallpaperData wallpaper) {
if (wallpaper.connection != null) {
+ if (DEBUG) {
+ Slog.v(TAG, "Detaching wallpaper: " + wallpaper);
+ }
if (wallpaper.connection.mReply != null) {
try {
wallpaper.connection.mReply.sendResult(null);
@@ -2882,7 +3277,11 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed detaching wallpaper service ", e);
}
- mContext.unbindService(wallpaper.connection);
+ try {
+ mContext.unbindService(wallpaper.connection);
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Attempted to unbind unregistered service");
+ }
wallpaper.connection.forEachDisplayConnector(DisplayConnector::disconnectLocked);
wallpaper.connection.mService = null;
wallpaper.connection.mDisplayConnector.clear();
@@ -3192,6 +3591,9 @@
}
/**
+ * Determines and returns the current wallpaper for the given user and destination, creating
+ * a valid entry if it does not already exist and adding it to the appropriate wallpaper map.
+ *
* Sometimes it is expected the wallpaper map may not have a user's data. E.g. This could
* happen during user switch. The async user switch observer may not have received
* the event yet. We use this safe method when we don't care about this ordering and just
@@ -3215,10 +3617,10 @@
// unified lock, so we bring up the saved state lazily now and recheck.
loadSettingsLocked(userId, false);
wallpaper = whichSet.get(userId);
- // if it's still null here, this is a lock-only operation and there is not
- // yet a lock-only wallpaper set for this user, so we need to establish
- // it now.
if (wallpaper == null) {
+ // if it's still null here, this is likely a lock-only operation and there is not
+ // currently a lock-only wallpaper set for this user, so we need to establish
+ // it now.
if (which == FLAG_LOCK) {
wallpaper = new WallpaperData(userId, FLAG_LOCK);
mLockWallpaperMap.put(userId, wallpaper);
@@ -3341,6 +3743,10 @@
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper != null) {
ensureSaneWallpaperData(lockWallpaper);
+ lockWallpaper.mWhich = FLAG_LOCK;
+ wallpaper.mWhich = FLAG_SYSTEM;
+ } else {
+ wallpaper.mWhich = FLAG_SYSTEM | FLAG_LOCK;
}
}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index e145898..cd48f5d 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -51,6 +51,9 @@
/**
* System service for managing sensing {@link AmbientContextEvent}s on Wearables.
+ *
+ * <p>The use of "Wearable" here is not the same as the Android Wear platform and should be treated
+ * separately. </p>
*/
public class WearableSensingManagerService extends
AbstractMasterSystemService<WearableSensingManagerService,
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index d4895ed..316b12a 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -762,7 +762,8 @@
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
return r != null
- ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ ? r.getOverrideOrientation()
+ : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 90ac1aa..24fe518 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -249,6 +249,7 @@
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.ICompatCameraControlCallback;
+import android.app.IScreenCaptureObserver;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
@@ -301,6 +302,7 @@
import android.os.LocaleList;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -886,6 +888,8 @@
private AppSaturationInfo mLastAppSaturationInfo;
+ private RemoteCallbackList<IScreenCaptureObserver> mCaptureCallbacks;
+
private final ColorDisplayService.ColorTransformController mColorTransformController =
(matrix, translation) -> mWmService.mH.post(() -> {
synchronized (mWmService.mGlobalLock) {
@@ -1168,8 +1172,10 @@
pw.println(prefix + "mVoiceInteraction=true");
}
pw.print(prefix); pw.print("mOccludesParent="); pw.println(mOccludesParent);
- pw.print(prefix); pw.print("mOrientation=");
- pw.println(ActivityInfo.screenOrientationToString(mOrientation));
+ pw.print(prefix); pw.print("overrideOrientation=");
+ pw.println(ActivityInfo.screenOrientationToString(getOverrideOrientation()));
+ pw.print(prefix); pw.print("requestedOrientation=");
+ pw.println(ActivityInfo.screenOrientationToString(super.getOverrideOrientation()));
pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
+ " mVisible=" + mVisible + " mClientVisible=" + isClientVisible()
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -1502,6 +1508,13 @@
}
@Override
+ boolean canStartChangeTransition() {
+ final Task task = getTask();
+ // Skip change transition when the Task is drag resizing.
+ return task != null && !task.isDragResizing() && super.canStartChangeTransition();
+ }
+
+ @Override
void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
final TaskFragment oldParent = (TaskFragment) rawOldParent;
final TaskFragment newParent = (TaskFragment) rawNewParent;
@@ -1956,6 +1969,15 @@
new ComponentName(info.packageName, info.targetActivity);
}
+ // Don't move below setActivityType since it triggers onConfigurationChange ->
+ // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
+ // Don't move below setOrientation(info.screenOrientation) since it triggers
+ // getOverrideOrientation that requires having mLetterboxUiController
+ // initialised.
+ mLetterboxUiController = new LetterboxUiController(mWmService, this);
+ mCameraCompatControlEnabled = mWmService.mContext.getResources()
+ .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
+
mTargetSdk = info.applicationInfo.targetSdkVersion;
mShowForAllUsers = (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
setOrientation(info.screenOrientation);
@@ -2076,12 +2098,6 @@
launchMode = aInfo.launchMode;
- // Don't move below setActivityType since it triggers onConfigurationChange ->
- // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
- mLetterboxUiController = new LetterboxUiController(mWmService, this);
- mCameraCompatControlEnabled = mWmService.mContext.getResources()
- .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
-
setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
@@ -2495,7 +2511,8 @@
if (topAttached != null) {
if (topAttached.isSnapshotCompatible(snapshot)
// This trampoline must be the same rotation.
- && mDisplayContent.getDisplayRotation().rotationForOrientation(mOrientation,
+ && mDisplayContent.getDisplayRotation().rotationForOrientation(
+ getOverrideOrientation(),
mDisplayContent.getRotation()) == snapshot.getRotation()) {
return STARTING_WINDOW_TYPE_SNAPSHOT;
}
@@ -7020,6 +7037,41 @@
return mLocusId;
}
+ public void reportScreenCaptured() {
+ if (mCaptureCallbacks != null) {
+ final int n = mCaptureCallbacks.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IScreenCaptureObserver obs = mCaptureCallbacks.getBroadcastItem(i);
+ try {
+ obs.onScreenCaptured();
+ } catch (RemoteException e) {
+ }
+ }
+ mCaptureCallbacks.finishBroadcast();
+ }
+ }
+
+ public void registerCaptureObserver(IScreenCaptureObserver observer) {
+ synchronized (mWmService.mGlobalLock) {
+ if (mCaptureCallbacks == null) {
+ mCaptureCallbacks = new RemoteCallbackList<IScreenCaptureObserver>();
+ }
+ mCaptureCallbacks.register(observer);
+ }
+ }
+
+ public void unregisterCaptureObserver(IScreenCaptureObserver observer) {
+ synchronized (mWmService.mGlobalLock) {
+ if (mCaptureCallbacks != null) {
+ mCaptureCallbacks.unregister(observer);
+ }
+ }
+ }
+
+ boolean isRegisteredForScreenCaptureCallback() {
+ return mCaptureCallbacks != null && mCaptureCallbacks.getRegisteredCallbackCount() > 0;
+ }
+
void setVoiceSessionLocked(IVoiceInteractionSession session) {
voiceSession = session;
pendingVoiceInteractionStart = false;
@@ -7720,13 +7772,13 @@
return mLetterboxUiController.getInheritedOrientation();
}
}
- if (mOrientation == SCREEN_ORIENTATION_BEHIND && task != null) {
+ if (task != null && getOverrideOrientation() == SCREEN_ORIENTATION_BEHIND) {
// We use Task here because we want to be consistent with what happens in
// multi-window mode where other tasks orientations are ignored.
final ActivityRecord belowCandidate = task.getActivity(
- a -> a.mOrientation != SCREEN_ORIENTATION_UNSET && !a.finishing
- && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND, this,
- false /* includeBoundary */, true /* traverseTopToBottom */);
+ a -> a.canDefineOrientationForActivitiesAbove() /* callback */,
+ this /* boundary */, false /* includeBoundary */,
+ true /* traverseTopToBottom */);
if (belowCandidate != null) {
return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
}
@@ -7734,6 +7786,19 @@
return super.getRequestedConfigurationOrientation(forDisplay);
}
+ /**
+ * Whether this activity can be used as an orientation source for activities above with
+ * {@link SCREEN_ORIENTATION_BEHIND}.
+ */
+ boolean canDefineOrientationForActivitiesAbove() {
+ if (finishing) {
+ return false;
+ }
+ final int overrideOrientation = getOverrideOrientation();
+ return overrideOrientation != SCREEN_ORIENTATION_UNSET
+ && overrideOrientation != SCREEN_ORIENTATION_BEHIND;
+ }
+
@Override
void onCancelFixedRotationTransform(int originalDisplayRotation) {
if (this != mDisplayContent.getLastOrientationSource()) {
@@ -7760,7 +7825,7 @@
}
}
- void setRequestedOrientation(int requestedOrientation) {
+ void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
return;
}
@@ -7803,7 +7868,7 @@
@VisibleForTesting
boolean shouldIgnoreOrientationRequests() {
if (!mAppActivityEmbeddingSplitsEnabled
- || !ActivityInfo.isFixedOrientationPortrait(mOrientation)
+ || !ActivityInfo.isFixedOrientationPortrait(getOverrideOrientation())
|| task.inMultiWindowMode()) {
return false;
}
@@ -7826,7 +7891,7 @@
// Allow app to specify orientation regardless of its visibility state if the current
// candidate want us to use orientation behind. I.e. the visible app on-top of this one
// wants us to use the orientation of the app behind it.
- return mOrientation;
+ return getOverrideOrientation();
}
// The {@link ActivityRecord} should only specify an orientation when it is not closing.
@@ -7834,15 +7899,31 @@
// task being started in the wrong orientation during the transition.
if (!getDisplayContent().mClosingApps.contains(this)
&& (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
- return mOrientation;
+ return getOverrideOrientation();
}
return SCREEN_ORIENTATION_UNSET;
}
- /** Returns the app's preferred orientation regardless of its currently visibility state. */
+ /**
+ * Returns the app's preferred orientation regardless of its current visibility state taking
+ * into account orientation per-app overrides applied by the device manufacturers.
+ */
+ @Override
+ protected int getOverrideOrientation() {
+ return mLetterboxUiController.overrideOrientationIfNeeded(super.getOverrideOrientation());
+ }
+
+ /**
+ * Returns the app's preferred orientation regardless of its currently visibility state. This
+ * is used to return a requested value to an app if they call {@link
+ * android.app.Activity#getRequestedOrientation} since {@link #getOverrideOrientation} value
+ * with override can confuse an app if it's different from what they requested with {@link
+ * android.app.Activity#setRequestedOrientation}.
+ */
+ @ActivityInfo.ScreenOrientation
int getRequestedOrientation() {
- return mOrientation;
+ return super.getOverrideOrientation();
}
/**
@@ -8405,8 +8486,8 @@
// If orientation is respected when insets are applied, then stableBounds will be empty.
boolean orientationRespectedWithInsets =
orientationRespectedWithInsets(parentBounds, stableBounds);
- if (orientationRespectedWithInsets
- && handlesOrientationChangeFromDescendant(mOrientation)) {
+ if (orientationRespectedWithInsets && handlesOrientationChangeFromDescendant(
+ getOverrideOrientation())) {
// No need to letterbox because of fixed orientation. Display will handle
// fixed-orientation requests and a display rotation is enough to respect requested
// orientation with insets applied.
@@ -9044,7 +9125,8 @@
}
if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
- && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation())) {
+ && !ActivityInfo.isFixedOrientationPortrait(
+ getOverrideOrientation())) {
return info.getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 64af9dd..91c4a2f 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -43,9 +43,12 @@
static final String DOC_LINK = "go/android-asm";
private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
- private static final String KEY_ASM_RESTRICTIONS_ENABLED = "asm_restrictions_enabled";
- private static final String KEY_ASM_TOASTS_ENABLED = "asm_toasts_enabled";
- private static final String KEY_ASM_EXEMPTED_PACKAGES = "asm_exempted_packages";
+ private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
+ private static final String KEY_ASM_RESTRICTIONS_ENABLED = KEY_ASM_PREFIX
+ + "asm_restrictions_enabled";
+ private static final String KEY_ASM_TOASTS_ENABLED = KEY_ASM_PREFIX + "asm_toasts_enabled";
+ private static final String KEY_ASM_EXEMPTED_PACKAGES = KEY_ASM_PREFIX
+ + "asm_exempted_packages";
private static final int VALUE_DISABLE = 0;
private static final int VALUE_ENABLE_FOR_U = 1;
private static final int VALUE_ENABLE_FOR_ALL = 2;
@@ -72,7 +75,7 @@
}
@GuardedBy("ActivityTaskManagerService.mGlobalLock")
- static boolean shouldBlockActivityStart(int uid) {
+ static boolean shouldRestrictActivitySwitch(int uid) {
return flagEnabledForUid(sAsmRestrictionsEnabled, uid);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d88f719..50eb356 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1965,14 +1965,14 @@
/* action */
action,
/* version */
- 1,
+ 2,
/* multi_window - we have our source not in the target task, but both are visible */
targetTask != null && mSourceRecord != null
&& !targetTask.equals(mSourceRecord.getTask()) && targetTask.isVisible()
);
boolean shouldBlockActivityStart =
- ActivitySecurityModelFeatureFlags.shouldBlockActivityStart(mCallingUid);
+ ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(mCallingUid);
if (ActivitySecurityModelFeatureFlags.shouldShowToast(mCallingUid)) {
UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
@@ -2080,6 +2080,7 @@
reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants);
if (mAddingToTask) {
+ clearTopIfNeeded(targetTask, mCallingUid, mStartActivity.getUid(), mLaunchFlags);
return START_SUCCESS;
}
@@ -2112,6 +2113,55 @@
}
/**
+ * If the top activity uid does not match the launched activity, and the launch was not
+ * requested from the top uid, we want to clear out all non matching activities to prevent the
+ * top activity being sandwiched.
+ */
+ private void clearTopIfNeeded(@NonNull Task targetTask, int callingUid, int startingUid,
+ int launchFlags) {
+ if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != FLAG_ACTIVITY_NEW_TASK) {
+ // Launch is from the same task, so must be a top or privileged UID
+ return;
+ }
+
+ ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity();
+ if (targetTaskTop != null && targetTaskTop.getUid() != startingUid) {
+ boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
+ .shouldRestrictActivitySwitch(callingUid);
+ int[] finishCount = new int[0];
+ if (shouldBlockActivityStart) {
+ ActivityRecord activity = targetTask.getActivity(
+ ar -> !ar.finishing && ar.isUid(startingUid));
+
+ if (activity == null) {
+ // mStartActivity is not in task, so clear everything
+ activity = mStartActivity;
+ }
+
+ finishCount = new int[1];
+ if (activity != null) {
+ targetTask.performClearTop(activity, launchFlags, finishCount);
+ }
+
+ if (finishCount[0] > 0) {
+ Slog.w(TAG, "Clearing top n: " + finishCount[0] + " activities from task t: "
+ + targetTask + " not matching top uid: " + callingUid);
+ }
+ }
+
+ if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)
+ && (!shouldBlockActivityStart || finishCount[0] > 0)) {
+ UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
+ (shouldBlockActivityStart
+ ? "Top activities cleared by "
+ : "Top activities would be cleared by ")
+ + ActivitySecurityModelFeatureFlags.DOC_LINK,
+ Toast.LENGTH_SHORT).show());
+ }
+ }
+ }
+
+ /**
* Check if the activity being launched is the same as the one currently at the top and it
* should only be launched once.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a927ed3..2f82167 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.DETECT_SCREEN_CAPTURE;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
@@ -145,6 +146,7 @@
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
+import android.app.IScreenCaptureObserver;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -5437,6 +5439,32 @@
}
}
+ @Override
+ public void registerScreenCaptureObserver(IBinder activityToken,
+ IScreenCaptureObserver observer) {
+ mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,
+ "registerScreenCaptureObserver");
+ synchronized (mGlobalLock) {
+ ActivityRecord activityRecord = ActivityRecord.forTokenLocked(activityToken);
+ if (activityRecord != null) {
+ activityRecord.registerCaptureObserver(observer);
+ }
+ }
+ }
+
+ @Override
+ public void unregisterScreenCaptureObserver(IBinder activityToken,
+ IScreenCaptureObserver observer) {
+ mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,
+ "unregisterScreenCaptureObserver");
+ synchronized (mGlobalLock) {
+ ActivityRecord activityRecord = ActivityRecord.forTokenLocked(activityToken);
+ if (activityRecord != null) {
+ activityRecord.unregisterCaptureObserver(observer);
+ }
+ }
+ }
+
/**
* Returns {@code true} if the process represented by the pid passed as argument is
* instrumented and the instrumentation source was granted with the permission also
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 8149e1c..0f1f51f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -137,6 +137,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Display;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -147,6 +148,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.UiThread;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.HostingRecord;
import com.android.server.am.UserState;
@@ -1628,16 +1630,16 @@
// Prevent recursion.
return;
}
+ boolean passesAsmChecks = true;
// We may have already checked that the callingUid has additional clearTask privileges, and
// cleared the calling identify. If so, we infer we do not need further restrictions here.
// TODO(b/263368846) Move to live with the rest of the ASM logic.
if (callingUid != SYSTEM_UID) {
- boolean passesAsmChecks = doesTopActivityMatchingUidExistForAsm(task, callingUid,
+ passesAsmChecks = doesTopActivityMatchingUidExistForAsm(task, callingUid,
null);
if (!passesAsmChecks) {
ActivityRecord topActivity = task.getActivity(ar ->
!ar.isState(FINISHING) && !ar.isAlwaysOnTop());
- Slog.i(TAG, "Finishing task from background. t: " + task);
FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
/* caller_uid */
callingUid,
@@ -1676,6 +1678,28 @@
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
+ if (!passesAsmChecks) {
+ boolean shouldRestrictActivitySwitch =
+ ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(callingUid);
+
+ if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
+ UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
+ (shouldRestrictActivitySwitch
+ ? "Returning home due to "
+ : "Would return home due to ")
+ + ActivitySecurityModelFeatureFlags.DOC_LINK,
+ Toast.LENGTH_SHORT).show());
+ }
+
+ // If the activity switch should be restricted, return home rather than the
+ // previously top task, to prevent users from being confused which app they're
+ // viewing
+ if (shouldRestrictActivitySwitch) {
+ Slog.w(TAG, "Return to home as source uid: " + callingUid
+ + "is not on top of task t: " + task);
+ task.getTaskDisplayArea().moveHomeActivityToTop("taskRemoved");
+ }
+ }
} finally {
task.mInRemoveTask = false;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d65c2f9..2b8b59c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -901,9 +901,18 @@
* TODO(b/213312721): Remove this predicate and its callers once ShellTransition is enabled.
*/
static boolean isTaskViewTask(WindowContainer wc) {
- // We use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
+ // Use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
// it is not guaranteed to work this logic in the future version.
- return wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer;
+ boolean isTaskViewTask = wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer;
+ if (isTaskViewTask) {
+ return true;
+ }
+
+ WindowContainer parent = wc.getParent();
+ boolean isParentATaskViewTask = parent != null
+ && parent instanceof Task
+ && ((Task) parent).mRemoveWithTaskOrganizer;
+ return isParentATaskViewTask;
}
/**
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 7aa734b..cd79f2e 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -86,7 +86,8 @@
/** Apps that fulfill a certain role that can can always launch new tasks */
static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = 3;
- /** Apps which currently have a visible window */
+ /** Apps which currently have a visible window or are bound by a service with a visible
+ * window */
static final int BAL_ALLOW_VISIBLE_WINDOW = 4;
/** Allowed due to the PendingIntent sender */
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index bdb06a97..63dc7d2 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -26,6 +26,7 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_BAL_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
import static java.util.Objects.requireNonNull;
@@ -152,7 +153,7 @@
Slog.d(TAG, "[Process(" + pid
+ ")] Activity start allowed: process bound by foreground uid");
}
- return BAL_ALLOW_FOREGROUND;
+ return BAL_ALLOW_VISIBLE_WINDOW;
}
// Allow if the flag was explicitly set.
if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java
index a6f8557..7d9a4ec 100644
--- a/services/core/java/com/android/server/wm/DeviceStateController.java
+++ b/services/core/java/com/android/server/wm/DeviceStateController.java
@@ -16,80 +16,107 @@
package com.android.server.wm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Handler;
import android.os.HandlerExecutor;
+import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
- * Class that registers callbacks with the {@link DeviceStateManager} and
- * responds to fold state changes by forwarding such events to a delegate.
+ * Class that registers callbacks with the {@link DeviceStateManager} and responds to device
+ * changes.
*/
-final class DeviceStateController {
+final class DeviceStateController implements DeviceStateManager.DeviceStateCallback {
+
+ @NonNull
private final DeviceStateManager mDeviceStateManager;
- private final Context mContext;
+ @NonNull
+ private final int[] mOpenDeviceStates;
+ @NonNull
+ private final int[] mHalfFoldedDeviceStates;
+ @NonNull
+ private final int[] mFoldedDeviceStates;
+ @NonNull
+ private final int[] mRearDisplayDeviceStates;
+ @NonNull
+ private final int[] mReverseRotationAroundZAxisStates;
+ @NonNull
+ private final List<Consumer<DeviceState>> mDeviceStateCallbacks = new ArrayList<>();
- private FoldStateListener mDeviceStateListener;
+ @Nullable
+ private DeviceState mLastDeviceState;
+ private int mCurrentState;
- public enum FoldState {
- UNKNOWN, OPEN, FOLDED, HALF_FOLDED
+ public enum DeviceState {
+ UNKNOWN, OPEN, FOLDED, HALF_FOLDED, REAR,
}
- DeviceStateController(Context context, Handler handler, Consumer<FoldState> delegate) {
- mContext = context;
- mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class);
+ DeviceStateController(@NonNull Context context, @NonNull Handler handler) {
+ mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+
+ mOpenDeviceStates = context.getResources()
+ .getIntArray(R.array.config_openDeviceStates);
+ mHalfFoldedDeviceStates = context.getResources()
+ .getIntArray(R.array.config_halfFoldedDeviceStates);
+ mFoldedDeviceStates = context.getResources()
+ .getIntArray(R.array.config_foldedDeviceStates);
+ mRearDisplayDeviceStates = context.getResources()
+ .getIntArray(R.array.config_rearDisplayDeviceStates);
+ mReverseRotationAroundZAxisStates = context.getResources()
+ .getIntArray(R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis);
+
if (mDeviceStateManager != null) {
- mDeviceStateListener = new FoldStateListener(mContext, delegate);
- mDeviceStateManager
- .registerCallback(new HandlerExecutor(handler),
- mDeviceStateListener);
+ mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
}
}
void unregisterFromDeviceStateManager() {
- if (mDeviceStateListener != null) {
- mDeviceStateManager.unregisterCallback(mDeviceStateListener);
+ if (mDeviceStateManager != null) {
+ mDeviceStateManager.unregisterCallback(this);
}
}
+ void registerDeviceStateCallback(@NonNull Consumer<DeviceState> callback) {
+ mDeviceStateCallbacks.add(callback);
+ }
+
/**
- * A listener for half-fold device state events that dispatches state changes to a delegate.
+ * @return true if the rotation direction on the Z axis should be reversed.
*/
- static final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
+ boolean shouldReverseRotationDirectionAroundZAxis() {
+ return ArrayUtils.contains(mReverseRotationAroundZAxisStates, mCurrentState);
+ }
- private final int[] mHalfFoldedDeviceStates;
- private final int[] mFoldedDeviceStates;
+ @Override
+ public void onStateChanged(int state) {
+ mCurrentState = state;
- @Nullable
- private FoldState mLastResult;
- private final Consumer<FoldState> mDelegate;
-
- FoldStateListener(Context context, Consumer<FoldState> delegate) {
- mFoldedDeviceStates = context.getResources().getIntArray(
- com.android.internal.R.array.config_foldedDeviceStates);
- mHalfFoldedDeviceStates = context.getResources().getIntArray(
- com.android.internal.R.array.config_halfFoldedDeviceStates);
- mDelegate = delegate;
+ final DeviceState deviceState;
+ if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
+ deviceState = DeviceState.HALF_FOLDED;
+ } else if (ArrayUtils.contains(mFoldedDeviceStates, state)) {
+ deviceState = DeviceState.FOLDED;
+ } else if (ArrayUtils.contains(mRearDisplayDeviceStates, state)) {
+ deviceState = DeviceState.REAR;
+ } else if (ArrayUtils.contains(mOpenDeviceStates, state)) {
+ deviceState = DeviceState.OPEN;
+ } else {
+ deviceState = DeviceState.UNKNOWN;
}
- @Override
- public void onStateChanged(int state) {
- final boolean halfFolded = ArrayUtils.contains(mHalfFoldedDeviceStates, state);
- FoldState result;
- if (halfFolded) {
- result = FoldState.HALF_FOLDED;
- } else {
- final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
- result = folded ? FoldState.FOLDED : FoldState.OPEN;
- }
- if (mLastResult == null || !mLastResult.equals(result)) {
- mLastResult = result;
- mDelegate.accept(result);
+ if (mLastDeviceState == null || !mLastDeviceState.equals(deviceState)) {
+ mLastDeviceState = deviceState;
+
+ for (Consumer<DeviceState> callback : mDeviceStateCallbacks) {
+ callback.accept(mLastDeviceState);
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index a15453e..de63191 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -94,7 +94,7 @@
DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
super(wms);
// TODO(display-area): move this up to ConfigurationContainer
- mOrientation = SCREEN_ORIENTATION_UNSET;
+ setOverrideOrientation(SCREEN_ORIENTATION_UNSET);
mType = type;
mName = name;
mFeatureId = featureId;
@@ -166,7 +166,8 @@
// If this is set to ignore the orientation request, we don't propagate descendant
// orientation request.
final int orientation = requestingContainer != null
- ? requestingContainer.mOrientation : SCREEN_ORIENTATION_UNSET;
+ ? requestingContainer.getOverrideOrientation()
+ : SCREEN_ORIENTATION_UNSET;
return !getIgnoreOrientationRequest(orientation)
&& super.onDescendantOrientationChanged(requestingContainer);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0bd59a8..f183760 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -27,6 +27,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -1145,14 +1146,18 @@
mWmService.mAtmService.getRecentTasks().getInputListener());
}
- mDisplayPolicy = new DisplayPolicy(mWmService, this);
- mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address);
+ mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);
- mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH,
- newFoldState -> {
+ mDisplayPolicy = new DisplayPolicy(mWmService, this);
+ mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address,
+ mDeviceStateController);
+
+ final Consumer<DeviceStateController.DeviceState> deviceStateConsumer =
+ (@NonNull DeviceStateController.DeviceState newFoldState) -> {
mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
mDisplayRotation.foldStateChanged(newFoldState);
- });
+ };
+ mDeviceStateController.registerDeviceStateCallback(deviceStateConsumer);
mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
R.dimen.config_closeToSquareDisplayMaxAspectRatio);
@@ -1618,7 +1623,8 @@
// If display rotation class tells us that it doesn't consider app requested orientation,
// this display won't rotate just because of an app changes its requested orientation. Thus
// it indicates that this display chooses not to handle this request.
- final int orientation = requestingContainer != null ? requestingContainer.mOrientation
+ final int orientation = requestingContainer != null
+ ? requestingContainer.getOverrideOrientation()
: SCREEN_ORIENTATION_UNSET;
final boolean handled = handlesOrientationChangeFromDescendant(orientation);
if (config == null) {
@@ -1744,14 +1750,17 @@
if (mTransitionController.useShellTransitionsRotation()) {
return ROTATION_UNDEFINED;
}
+ final int activityOrientation = r.getOverrideOrientation();
if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM
- || getIgnoreOrientationRequest(r.mOrientation)) {
+ || getIgnoreOrientationRequest(activityOrientation)) {
return ROTATION_UNDEFINED;
}
- if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ if (activityOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ // TODO(b/266280737): Use ActivityRecord#canDefineOrientationForActivitiesAbove
final ActivityRecord nextCandidate = getActivity(
- a -> a.mOrientation != SCREEN_ORIENTATION_UNSET
- && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND,
+ a -> a.getOverrideOrientation() != SCREEN_ORIENTATION_UNSET
+ && a.getOverrideOrientation()
+ != ActivityInfo.SCREEN_ORIENTATION_BEHIND,
r, false /* includeBoundary */, true /* traverseTopToBottom */);
if (nextCandidate != null) {
r = nextCandidate;
@@ -2774,6 +2783,15 @@
final int orientation = super.getOrientation();
if (!handlesOrientationChangeFromDescendant(orientation)) {
+ ActivityRecord topActivity = topRunningActivity(/* considerKeyguardState= */ true);
+ if (topActivity != null && topActivity.mLetterboxUiController
+ .shouldUseDisplayLandscapeNaturalOrientation()) {
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Display id=%d is ignoring orientation request for %d, return %d"
+ + " following a per-app override for %s",
+ mDisplayId, orientation, SCREEN_ORIENTATION_LANDSCAPE, topActivity);
+ return SCREEN_ORIENTATION_LANDSCAPE;
+ }
mLastOrientationSource = null;
// Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
ProtoLog.v(WM_DEBUG_ORIENTATION,
@@ -5264,6 +5282,11 @@
return b;
}
+ // WARNING: it says `mSurfaceControl` below, but this CHANGES meaning after construction!
+ // DisplayAreas are added in `configureSurface()` *before* `mSurfaceControl` gets replaced
+ // with a wrapper or magnification surface so they end up in the right place; however,
+ // anything added or reparented to "the display" *afterwards* needs to be reparented to
+ // `getWindowinglayer()` (unless it's an overlay DisplayArea).
return b.setName(child.getName())
.setParent(mSurfaceControl);
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index e6d8b3d..6f7ff5c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -41,6 +41,7 @@
import android.annotation.AnimRes;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -117,6 +118,8 @@
private SettingsObserver mSettingsObserver;
@Nullable
private FoldController mFoldController;
+ @NonNull
+ private final DeviceStateController mDeviceStateController;
@ScreenOrientation
private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
@@ -218,21 +221,24 @@
private boolean mDemoRotationLock;
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
- DisplayAddress displayAddress) {
+ DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController) {
this(service, displayContent, displayAddress, displayContent.getDisplayPolicy(),
- service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
+ service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock(),
+ deviceStateController);
}
@VisibleForTesting
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
DisplayAddress displayAddress, DisplayPolicy displayPolicy,
- DisplayWindowSettings displayWindowSettings, Context context, Object lock) {
+ DisplayWindowSettings displayWindowSettings, Context context, Object lock,
+ @NonNull DeviceStateController deviceStateController) {
mService = service;
mDisplayContent = displayContent;
mDisplayPolicy = displayPolicy;
mDisplayWindowSettings = displayWindowSettings;
mContext = context;
mLock = lock;
+ mDeviceStateController = deviceStateController;
isDefaultDisplay = displayContent.isDefaultDisplay;
mCompatPolicyForImmersiveApps = initImmersiveAppCompatPolicy(service, displayContent);
@@ -1137,6 +1143,15 @@
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
+ if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
+ // Flipping 270 and 90 has the same effect as changing the direction which rotation is
+ // applied.
+ if (sensorRotation == Surface.ROTATION_90) {
+ sensorRotation = Surface.ROTATION_270;
+ } else if (sensorRotation == Surface.ROTATION_270) {
+ sensorRotation = Surface.ROTATION_90;
+ }
+ }
mLastSensorRotation = sensorRotation;
if (sensorRotation < 0) {
sensorRotation = lastRotation;
@@ -1573,7 +1588,7 @@
proto.end(token);
}
- boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
+ boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
if (mFoldController == null) return false;
return mFoldController.isDeviceInPosture(state, isTabletop);
}
@@ -1585,10 +1600,10 @@
/**
* Called by the DeviceStateManager callback when the device state changes.
*/
- void foldStateChanged(DeviceStateController.FoldState foldState) {
+ void foldStateChanged(DeviceStateController.DeviceState deviceState) {
if (mFoldController != null) {
synchronized (mLock) {
- mFoldController.foldStateChanged(foldState);
+ mFoldController.foldStateChanged(deviceState);
}
}
}
@@ -1596,8 +1611,8 @@
private class FoldController {
@Surface.Rotation
private int mHalfFoldSavedRotation = -1; // No saved rotation
- private DeviceStateController.FoldState mFoldState =
- DeviceStateController.FoldState.UNKNOWN;
+ private DeviceStateController.DeviceState mDeviceState =
+ DeviceStateController.DeviceState.UNKNOWN;
private boolean mInHalfFoldTransition = false;
private final boolean mIsDisplayAlwaysSeparatingHinge;
private final Set<Integer> mTabletopRotations;
@@ -1637,32 +1652,33 @@
R.bool.config_isDisplayHingeAlwaysSeparating);
}
- boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
- if (state != mFoldState) {
+ boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
+ if (state != mDeviceState) {
return false;
}
- if (mFoldState == DeviceStateController.FoldState.HALF_FOLDED) {
+ if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
return !(isTabletop ^ mTabletopRotations.contains(mRotation));
}
return true;
}
- DeviceStateController.FoldState getFoldState() {
- return mFoldState;
+ DeviceStateController.DeviceState getFoldState() {
+ return mDeviceState;
}
boolean isSeparatingHinge() {
- return mFoldState == DeviceStateController.FoldState.HALF_FOLDED
- || (mFoldState == DeviceStateController.FoldState.OPEN
+ return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED
+ || (mDeviceState == DeviceStateController.DeviceState.OPEN
&& mIsDisplayAlwaysSeparatingHinge);
}
boolean overrideFrozenRotation() {
- return mFoldState == DeviceStateController.FoldState.HALF_FOLDED;
+ return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
}
boolean shouldRevertOverriddenRotation() {
- return mFoldState == DeviceStateController.FoldState.OPEN // When transitioning to open.
+ // When transitioning to open.
+ return mDeviceState == DeviceStateController.DeviceState.OPEN
&& mInHalfFoldTransition
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
&& mUserRotationMode
@@ -1676,30 +1692,30 @@
return savedRotation;
}
- void foldStateChanged(DeviceStateController.FoldState newState) {
+ void foldStateChanged(DeviceStateController.DeviceState newState) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"foldStateChanged: displayId %d, halfFoldStateChanged %s, "
+ "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
+ "mLastOrientation: %d, mRotation: %d",
mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
- if (mFoldState == DeviceStateController.FoldState.UNKNOWN) {
- mFoldState = newState;
+ if (mDeviceState == DeviceStateController.DeviceState.UNKNOWN) {
+ mDeviceState = newState;
return;
}
- if (newState == DeviceStateController.FoldState.HALF_FOLDED
- && mFoldState != DeviceStateController.FoldState.HALF_FOLDED) {
+ if (newState == DeviceStateController.DeviceState.HALF_FOLDED
+ && mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
// The device has transitioned to HALF_FOLDED state: save the current rotation and
// update the device rotation.
mHalfFoldSavedRotation = mRotation;
- mFoldState = newState;
+ mDeviceState = newState;
// Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
// return true, so rotation is unlocked.
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
} else {
mInHalfFoldTransition = true;
- mFoldState = newState;
+ mDeviceState = newState;
// Tell the device to update its orientation.
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
@@ -1822,7 +1838,7 @@
final long mTimestamp = System.currentTimeMillis();
final int mHalfFoldSavedRotation;
final boolean mInHalfFoldTransition;
- final DeviceStateController.FoldState mFoldState;
+ final DeviceStateController.DeviceState mDeviceState;
@Nullable final String mDisplayRotationCompatPolicySummary;
Record(DisplayRotation dr, int fromRotation, int toRotation) {
@@ -1843,8 +1859,9 @@
if (source != null) {
mLastOrientationSource = source.toString();
final WindowState w = source.asWindowState();
- mSourceOrientation =
- w != null ? w.mAttrs.screenOrientation : source.mOrientation;
+ mSourceOrientation = w != null
+ ? w.mAttrs.screenOrientation
+ : source.getOverrideOrientation();
} else {
mLastOrientationSource = null;
mSourceOrientation = SCREEN_ORIENTATION_UNSET;
@@ -1852,11 +1869,11 @@
if (dr.mFoldController != null) {
mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
- mFoldState = dr.mFoldController.mFoldState;
+ mDeviceState = dr.mFoldController.mDeviceState;
} else {
mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
mInHalfFoldTransition = false;
- mFoldState = DeviceStateController.FoldState.UNKNOWN;
+ mDeviceState = DeviceStateController.DeviceState.UNKNOWN;
}
mDisplayRotationCompatPolicySummary = dc.mDisplayRotationCompatPolicy == null
? null
@@ -1882,7 +1899,7 @@
pw.println(prefix + " halfFoldSavedRotation="
+ mHalfFoldSavedRotation
+ " mInHalfFoldTransition=" + mInHalfFoldTransition
- + " mFoldState=" + mFoldState);
+ + " mFoldState=" + mDeviceState);
}
if (mDisplayRotationCompatPolicySummary != null) {
pw.println(prefix + mDisplayRotationCompatPolicySummary);
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index c6037da..3ffb2fa 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -296,8 +296,8 @@
&& activity.getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED
// "locked" and "nosensor" values are often used by camera apps that can't
// handle dynamic changes so we shouldn't force rotate them.
- && activity.getRequestedOrientation() != SCREEN_ORIENTATION_NOSENSOR
- && activity.getRequestedOrientation() != SCREEN_ORIENTATION_LOCKED
+ && activity.getOverrideOrientation() != SCREEN_ORIENTATION_NOSENSOR
+ && activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED
&& mCameraIdPackageBiMap.containsPackageName(activity.packageName)
&& activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e9badef..718a9e3 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -20,47 +20,63 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.KEYGUARD_STATE_AOD_SHOWN;
+import static android.view.WindowManager.KEYGUARD_STATE_DREAMING;
+import static android.view.WindowManager.KEYGUARD_STATE_GOING_AWAY;
+import static android.view.WindowManager.KEYGUARD_STATE_KEYGUARD_TOP;
+import static android.view.WindowManager.KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+import static android.view.WindowManager.KEYGUARD_STATE_OCCLUDED;
+import static android.view.WindowManager.KEYGUARD_STATE_OFF;
+import static android.view.WindowManager.KEYGUARD_STATE_ON;
+import static android.view.WindowManager.KEYGUARD_STATE_ROOT;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_WAKE;
+import static android.view.WindowManager.keyguardStateToString;
+import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
-import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_GOING_AWAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import android.view.WindowManager;
+import android.view.WindowManager.KeyguardState;
+import android.window.TransitionInfo;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.StateMachine;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
/**
* Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
@@ -69,281 +85,138 @@
* Note that everything in this class should only be accessed with the AM lock being held.
*/
class KeyguardController {
-
private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
+ private static final boolean DEBUG = true;
+
static final String KEYGUARD_SLEEP_TOKEN_TAG = "keyguard";
private static final int DEFER_WAKE_TRANSITION_TIMEOUT_MS = 5000;
- private final ActivityTaskSupervisor mTaskSupervisor;
- private WindowManagerService mWindowManager;
-
- private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
- private RootWindowContainer mRootWindowContainer;
+ private final ActivityTaskSupervisor mTaskSupervisor;
private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
private boolean mWaitingForWakeTransition;
+ private WindowManagerService mWindowManager;
+
+ private RootWindowContainer mRootWindowContainer;
+
+ private final SparseArray<DisplayState> mDisplayStates = new SparseArray<>();
+
+ @NonNull private final ServiceDelegate mServiceDelegate = new ServiceDelegate();
KeyguardController(ActivityTaskManagerService service,
ActivityTaskSupervisor taskSupervisor) {
mService = service;
mTaskSupervisor = taskSupervisor;
- mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
+ mSleepTokenAcquirer = service.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
}
void setWindowManager(WindowManagerService windowManager) {
mWindowManager = windowManager;
mRootWindowContainer = mService.mRootWindowContainer;
+ mService.getTransitionController().registerLegacyListener(
+ new WindowManagerInternal.AppTransitionListener() {
+ @Override
+ public int onAppTransitionStartingLocked(TransitionInfo info) {
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ if (changes.size() == 0) {
+ Slog.e(TAG, "TransitionInfo doesn't contain change: " + info);
+ return 0;
+ }
+ final ActivityManager.RunningTaskInfo taskInfo =
+ changes.get(0).getTaskInfo();
+ if (taskInfo == null) {
+ Slog.e(TAG, "No RunningTaskInfo: " + info);
+ return 0;
+ }
+
+ // TODO(b/242856311): Filtering condition is defined here and in SysUI
+ // Keyguard service, which need to be in sync. For a long term, we should
+ // define a new API for notifying occlude status from WMS to SysUI, and
+ // the filtering logic should only exist in WM Shell.
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) == 0) {
+ return 0;
+ }
+
+ boolean occludeOpeningApp = false;
+ boolean occludeClosingApp = false;
+ for (int i = 0; i < changes.size(); ++i) {
+ final TransitionInfo.Change change = changes.get(i);
+ if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) {
+ if (change.getMode() == TRANSIT_OPEN
+ || change.getMode() == TRANSIT_TO_FRONT) {
+ occludeOpeningApp = true;
+ }
+ if (change.getMode() == TRANSIT_CLOSE
+ || change.getMode() == TRANSIT_TO_BACK) {
+ occludeClosingApp = true;
+ }
+ }
+ }
+ final DisplayState state = getDisplayState(taskInfo.displayId);
+ if (occludeOpeningApp && !occludeClosingApp) {
+ state.commitOccludedStatus(true /* occluded */);
+ } else if (!occludeOpeningApp && occludeClosingApp) {
+ state.commitOccludedStatus(false /* occluded */);
+ }
+ return 0;
+ }
+ });
}
boolean isAodShowing(int displayId) {
- return getDisplayState(displayId).mAodShowing;
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_AOD_SHOWN);
}
/**
- * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
- * on the given display, false otherwise.
+ * @return {@code true} if either Keyguard or AOD are showing.
*/
boolean isKeyguardOrAodShowing(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- return (state.mKeyguardShowing || state.mAodShowing)
- && !state.mKeyguardGoingAway
- && !isDisplayOccluded(displayId);
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_KEYGUARD_TOP);
}
/**
- * @return {@code true} for default display when AOD is showing, not going away. Otherwise, same
- * as {@link #isKeyguardOrAodShowing(int)}
- * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic.
+ * @return {@codd true} if lock screen is showing.
*/
- boolean isKeyguardUnoccludedOrAodShowing(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- if (displayId == DEFAULT_DISPLAY && state.mAodShowing) {
- return !state.mKeyguardGoingAway;
- }
- return isKeyguardOrAodShowing(displayId);
+ boolean isLocksScreenShowing(int displayId) {
+ // NOTE: This is only used by WindowManagerService#notifyKeyguardTrustedChanged
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
}
/**
- * @return true if Keyguard is showing, not going away, and not being occluded on the given
- * display, false otherwise
- */
- boolean isKeyguardShowing(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- return state.mKeyguardShowing && !state.mKeyguardGoingAway
- && !isDisplayOccluded(displayId);
- }
-
- /**
- * @return true if Keyguard is either showing or occluded, but not going away
+ * @return {@code true} if Keyguard is either showing or occluded.
*/
boolean isKeyguardLocked(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- return state.mKeyguardShowing && !state.mKeyguardGoingAway;
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_ON);
}
/**
- *
* @return true if the activity is controlling keyguard state.
*/
boolean topActivityOccludesKeyguard(ActivityRecord r) {
- return getDisplayState(r.getDisplayId()).mTopOccludesActivity == r;
+ return getDisplayState(r.getDisplayId()).topActivityOccludesKeyguard(r);
}
/**
* @return {@code true} if the keyguard is going away, {@code false} otherwise.
*/
boolean isKeyguardGoingAway(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- // Also check keyguard showing in case value is stale.
- return state.mKeyguardGoingAway && state.mKeyguardShowing;
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_GOING_AWAY);
}
/**
- * Update the Keyguard showing state.
+ * Checks whether the top activity occludes the keyguard.
*/
- void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) {
- if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) {
- Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId);
- return;
- }
-
- final KeyguardDisplayState state = getDisplayState(displayId);
- final boolean aodChanged = aodShowing != state.mAodShowing;
- final boolean aodRemoved = state.mAodShowing && !aodShowing;
- // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
- // Do not reset keyguardChanged status when only AOD is removed.
- final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing)
- || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved);
- if (aodRemoved) {
- updateDeferTransitionForAod(false /* waiting */);
- }
- if (!keyguardChanged && !aodChanged) {
- setWakeTransitionReady();
- return;
- }
- EventLogTags.writeWmSetKeyguardShown(
- displayId,
- keyguardShowing ? 1 : 0,
- aodShowing ? 1 : 0,
- state.mKeyguardGoingAway ? 1 : 0,
- "setKeyguardShown");
-
- // Update the task snapshot if the screen will not be turned off. To make sure that the
- // unlocking animation can animate consistent content. The conditions are:
- // - Either AOD or keyguard changes to be showing. So if the states change individually,
- // the later one can be skipped to avoid taking snapshot again. While it still accepts
- // if both of them change to show at the same time.
- // - Keyguard was not going away. Because if it was, the closing transition is able to
- // handle the snapshot.
- // - The display state is ON. Because if AOD is not on or pulsing, the display state will
- // be OFF or DOZE (the path of screen off may have handled it).
- if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
- && !state.mKeyguardGoingAway && Display.isOnState(
- mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
- mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
- }
-
- state.mKeyguardShowing = keyguardShowing;
- state.mAodShowing = aodShowing;
-
- if (keyguardChanged) {
- // Irrelevant to AOD.
- state.mKeyguardGoingAway = false;
- if (keyguardShowing) {
- state.mDismissalRequested = false;
- }
- }
-
- // Update the sleep token first such that ensureActivitiesVisible has correct sleep token
- // state when evaluating visibilities.
- updateKeyguardSleepToken();
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */);
- setWakeTransitionReady();
- if (aodChanged) {
- // Ensure the new state takes effect.
- mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
-
- private void setWakeTransitionReady() {
- if (mWindowManager.mAtmService.getTransitionController().getCollectingTransitionType()
- == WindowManager.TRANSIT_WAKE) {
- mWindowManager.mAtmService.getTransitionController().setReady(
- mRootWindowContainer.getDefaultDisplay());
- }
+ boolean isDisplayOccluded(int displayId) {
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_OCCLUDED);
}
/**
- * Called when Keyguard is going away.
- *
- * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
- * etc.
+ * @return Whether the dream activity is on top of default display.
*/
- void keyguardGoingAway(int displayId, int flags) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- if (!state.mKeyguardShowing || state.mKeyguardGoingAway) {
- return;
- }
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
- mService.deferWindowLayout();
- state.mKeyguardGoingAway = true;
- try {
- EventLogTags.writeWmSetKeyguardShown(
- displayId,
- 1 /* keyguardShowing */,
- state.mAodShowing ? 1 : 0,
- 1 /* keyguardGoingAway */,
- "keyguardGoingAway");
- final int transitFlags = convertTransitFlags(flags);
- final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags);
- // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
- // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
- // away.
- dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
- TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc);
- updateKeyguardSleepToken();
-
- // Some stack visibility might change (e.g. docked stack)
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mRootWindowContainer.addStartingWindowsForVisibleActivities();
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
- void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
- final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
- if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
- failCallback(callback);
- return;
- }
- Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
-
- // If the client has requested to dismiss the keyguard and the Activity has the flag to
- // turn the screen on, wakeup the screen if it's the top Activity.
- if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
- mTaskSupervisor.wakeUp("dismissKeyguard");
- }
-
- mWindowManager.dismissKeyguard(callback, message);
- }
-
- private void failCallback(IKeyguardDismissCallback callback) {
- try {
- callback.onDismissError();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to call callback", e);
- }
- }
-
- private int convertTransitFlags(int keyguardGoingAwayFlags) {
- int result = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
- }
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
- }
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
- }
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
- }
- if ((keyguardGoingAwayFlags
- & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
- }
- return result;
- }
-
- /**
- * @return True if we may show an activity while Keyguard is showing because we are in the
- * process of dismissing it anyways, false otherwise.
- */
- boolean canShowActivityWhileKeyguardShowing(ActivityRecord r) {
- // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
- // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
- // Keyguard.
- final KeyguardDisplayState state = getDisplayState(r.getDisplayId());
- return r.containsDismissKeyguardWindow() && canDismissKeyguard() && !state.mAodShowing
- && (state.mDismissalRequested
- || (r.canShowWhenLocked() && state.mDismissingKeyguardActivity != r));
- }
-
- /**
- * @return True if we may show an activity while Keyguard is occluded, false otherwise.
- */
- boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
- return showWhenLocked || dismissKeyguard
- && !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
+ boolean isShowingDream() {
+ return getDisplayState(DEFAULT_DISPLAY).isIn(KEYGUARD_STATE_DREAMING);
}
/**
@@ -351,181 +224,15 @@
*
* @return true if {@param r} is visible taken Keyguard state into account, false otherwise
*/
- boolean checkKeyguardVisibility(ActivityRecord r) {
+ boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
if (r.mDisplayContent.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
return true;
}
-
- if (isKeyguardOrAodShowing(r.mDisplayContent.getDisplayId())) {
- // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
- // right away and AOD isn't visible.
- return canShowActivityWhileKeyguardShowing(r);
- } else if (isKeyguardLocked(r.getDisplayId())) {
- return canShowWhileOccluded(r.containsDismissKeyguardWindow(), r.canShowWhenLocked());
- } else {
- return true;
- }
- }
-
- /**
- * Makes sure to update lockscreen occluded/dismiss/turnScreenOn state if needed before
- * completing set all visibility
- * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}).
- */
- void updateVisibility() {
- for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
- displayNdx >= 0; displayNdx--) {
- final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
- if (display.isRemoving() || display.isRemoved()) continue;
- final KeyguardDisplayState state = getDisplayState(display.mDisplayId);
- state.updateVisibility(this, display);
- if (state.mRequestDismissKeyguard) {
- handleDismissKeyguard(display.getDisplayId());
- }
- }
- }
-
- /**
- * Called when occluded state changed.
- *
- * @param topActivity the activity that controls the state whether keyguard should
- * be occluded. That is the activity to be shown on top of keyguard if it requests so.
- */
- private void handleOccludedChanged(int displayId, @Nullable ActivityRecord topActivity) {
- // TODO(b/113840485): Handle app transition for individual display, and apply occluded
- // state change to secondary displays.
- // For now, only default display fully supports occluded change. Other displays only
- // updates keyguard sleep token on that display.
- if (displayId != DEFAULT_DISPLAY) {
- updateKeyguardSleepToken(displayId);
- return;
- }
-
- mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
- if (isKeyguardLocked(displayId)) {
- mService.deferWindowLayout();
- try {
- mRootWindowContainer.getDefaultDisplay()
- .requestTransitionAndLegacyPrepare(
- isDisplayOccluded(DEFAULT_DISPLAY)
- ? TRANSIT_KEYGUARD_OCCLUDE
- : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
- updateKeyguardSleepToken(DEFAULT_DISPLAY);
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
- }
- }
- dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null
- ? topActivity.getRootTask() : null);
- }
-
- /**
- * Called when keyguard going away state changed.
- */
- private void handleKeyguardGoingAwayChanged(DisplayContent dc) {
- mService.deferWindowLayout();
- try {
- dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, 0 /* transitFlags */);
- // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
- // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
- // away.
- dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
- TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, null /* trigger */, dc);
- updateKeyguardSleepToken();
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
- }
- }
-
- /**
- * Called when somebody wants to dismiss the Keyguard via the flag.
- */
- private void handleDismissKeyguard(int displayId) {
- // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
- // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
- // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
- if (!mWindowManager.isKeyguardSecure(mService.getCurrentUserId())) {
- return;
- }
-
- mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
- final KeyguardDisplayState state = getDisplayState(displayId);
- state.mDismissalRequested = true;
-
- // If we are about to unocclude the Keyguard, but we can dismiss it without security,
- // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
- final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- if (state.mKeyguardShowing && canDismissKeyguard()
- && dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) {
- mWindowManager.executeAppTransition();
- }
- }
-
- boolean isDisplayOccluded(int displayId) {
- return getDisplayState(displayId).mOccluded;
- }
-
- /**
- * @return true if Keyguard can be currently dismissed without entering credentials.
- */
- boolean canDismissKeyguard() {
- return mWindowManager.mPolicy.isKeyguardTrustedLw()
- || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
- }
-
- /**
- * @return Whether the dream activity is on top of default display.
- */
- boolean isShowingDream() {
- return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
- }
-
- private void dismissMultiWindowModeForTaskIfNeeded(int displayId,
- @Nullable Task currentTaskControllingOcclusion) {
- // TODO(b/113840485): Handle docked stack for individual display.
- if (!getDisplayState(displayId).mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
- return;
- }
-
- // Dismiss freeform windowing mode
- if (currentTaskControllingOcclusion == null) {
- return;
- }
- if (currentTaskControllingOcclusion.inFreeformWindowingMode()) {
- currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- }
- }
-
- private void updateKeyguardSleepToken() {
- for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
- displayNdx >= 0; displayNdx--) {
- final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
- updateKeyguardSleepToken(display.mDisplayId);
- }
- }
-
- private void updateKeyguardSleepToken(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- if (isKeyguardUnoccludedOrAodShowing(displayId)) {
- state.mSleepTokenAcquirer.acquire(displayId);
- } else {
- state.mSleepTokenAcquirer.release(displayId);
- }
- }
-
- private KeyguardDisplayState getDisplayState(int displayId) {
- KeyguardDisplayState state = mDisplayStates.get(displayId);
- if (state == null) {
- state = new KeyguardDisplayState(mService, displayId, mSleepTokenAcquirer);
- mDisplayStates.append(displayId, state);
- }
- return state;
+ return getDisplayState(r.mDisplayContent.getDisplayId()).checkKeyguardVisibility(r);
}
void onDisplayRemoved(int displayId) {
- final KeyguardDisplayState state = mDisplayStates.get(displayId);
+ final DisplayState state = mDisplayStates.get(displayId);
if (state != null) {
state.onRemoved();
mDisplayStates.remove(displayId);
@@ -538,7 +245,15 @@
}
};
- // Defer transition until AOD dismissed.
+ /**
+ * Update if app transition should be deferred until AOD state changes.
+ *
+ * <p>Note: This is used for defer app transition before the device fully wakes up, since during
+ * wake up process, activities life cycle can be messed up due to a display sleep token.
+ *
+ * @param waiting {@code true} to defer an app transition, {@code false} to continue an app
+ * transition.
+ */
void updateDeferTransitionForAod(boolean waiting) {
if (waiting == mWaitingForWakeTransition) {
return;
@@ -549,212 +264,1115 @@
// if AOD is showing, defer the wake transition until AOD state changed.
if (waiting && isAodShowing(DEFAULT_DISPLAY)) {
mWaitingForWakeTransition = true;
- mWindowManager.mAtmService.getTransitionController().deferTransitionReady();
+ mService.getTransitionController().deferTransitionReady();
mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS);
} else if (!waiting) {
// dismiss the deferring if the AOD state change or cancel awake.
mWaitingForWakeTransition = false;
- mWindowManager.mAtmService.getTransitionController().continueTransitionReady();
+ mService.getTransitionController().continueTransitionReady();
mWindowManager.mH.removeCallbacks(mResetWaitTransition);
}
}
-
- /** Represents Keyguard state per individual display. */
- private static class KeyguardDisplayState {
- private final int mDisplayId;
- private boolean mKeyguardShowing;
- private boolean mAodShowing;
- private boolean mKeyguardGoingAway;
- private boolean mDismissalRequested;
- private boolean mOccluded;
- private boolean mShowingDream;
-
- private ActivityRecord mTopOccludesActivity;
- private ActivityRecord mDismissingKeyguardActivity;
- private ActivityRecord mTopTurnScreenOnActivity;
-
- private boolean mRequestDismissKeyguard;
- private final ActivityTaskManagerService mService;
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
-
- KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
- ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
- mService = service;
- mDisplayId = displayId;
- mSleepTokenAcquirer = acquirer;
- }
-
- void onRemoved() {
- mTopOccludesActivity = null;
- mDismissingKeyguardActivity = null;
- mTopTurnScreenOnActivity = null;
- mSleepTokenAcquirer.release(mDisplayId);
- }
-
- /**
- * Updates keyguard status if the top task could be visible. The top task may occlude
- * keyguard, request to dismiss keyguard or make insecure keyguard go away based on its
- * properties.
- */
- void updateVisibility(KeyguardController controller, DisplayContent display) {
- final boolean lastOccluded = mOccluded;
- final boolean lastKeyguardGoingAway = mKeyguardGoingAway;
-
- final ActivityRecord lastDismissKeyguardActivity = mDismissingKeyguardActivity;
- final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity;
-
- mRequestDismissKeyguard = false;
- mOccluded = false;
- mShowingDream = false;
-
- mTopOccludesActivity = null;
- mDismissingKeyguardActivity = null;
- mTopTurnScreenOnActivity = null;
-
- boolean occludedByActivity = false;
- final Task task = getRootTaskForControllingOccluding(display);
- final ActivityRecord top = task != null ? task.getTopNonFinishingActivity() : null;
- if (top != null) {
- if (top.containsDismissKeyguardWindow()) {
- mDismissingKeyguardActivity = top;
- }
- if (top.getTurnScreenOnFlag() && top.currentLaunchCanTurnScreenOn()) {
- mTopTurnScreenOnActivity = top;
- }
-
- if (top.mDismissKeyguard && mKeyguardShowing) {
- mKeyguardGoingAway = true;
- } else if (top.canShowWhenLocked()) {
- mTopOccludesActivity = top;
- }
- top.mDismissKeyguard = false;
-
- // Only the top activity may control occluded, as we can't occlude the Keyguard
- // if the top app doesn't want to occlude it.
- occludedByActivity = mTopOccludesActivity != null
- || (mDismissingKeyguardActivity != null
- && task.topRunningActivity() == mDismissingKeyguardActivity
- && controller.canShowWhileOccluded(
- true /* dismissKeyguard */, false /* showWhenLocked */));
- // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
- if (mDisplayId != DEFAULT_DISPLAY) {
- occludedByActivity |= display.canShowWithInsecureKeyguard()
- && controller.canDismissKeyguard();
- }
+ /**
+ * TODO(b/242851358): Remove this function once SysUI migrate to the new API.
+ */
+ @KeyguardState private static int convertToState(int displayId, boolean keyguardShowing,
+ boolean aodShowing) {
+ if (displayId == DEFAULT_DISPLAY) {
+ if (aodShowing) {
+ return KEYGUARD_STATE_AOD_SHOWN;
+ } else if (keyguardShowing) {
+ return KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+ } else {
+ return KEYGUARD_STATE_OFF;
}
-
- mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null
- && top.getActivityType() == ACTIVITY_TYPE_DREAM);
- mOccluded = mShowingDream || occludedByActivity;
- mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity
- && !mOccluded && !mKeyguardGoingAway
- && mDismissingKeyguardActivity != null;
- if (mOccluded && mKeyguardShowing && !display.isSleeping() && !top.fillsParent()
- && display.mWallpaperController.getWallpaperTarget() == null) {
- // The occluding activity may be translucent or not fill screen. Then let wallpaper
- // to check whether it should set itself as target to avoid blank background.
- display.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ } else {
+ if (keyguardShowing || aodShowing) {
+ return KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+ } else {
+ return KEYGUARD_STATE_OFF;
}
-
- if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity
- && mTopTurnScreenOnActivity != null
- && !mService.mWindowManager.mPowerManager.isInteractive()
- && (mRequestDismissKeyguard || occludedByActivity)) {
- controller.mTaskSupervisor.wakeUp("handleTurnScreenOn");
- mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
- }
-
- boolean hasChange = false;
- if (lastOccluded != mOccluded) {
- controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
- hasChange = true;
- } else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
- controller.handleKeyguardGoingAwayChanged(display);
- hasChange = true;
- }
- // Collect the participates for shell transition, so that transition won't happen too
- // early since the transition was set ready.
- if (hasChange && top != null && (mOccluded || mKeyguardGoingAway)) {
- display.mTransitionController.collect(top);
- }
- }
-
- /**
- * Gets the stack used to check the occluded state.
- * <p>
- * Only the top non-pinned activity of the focusable stack on each display can control its
- * occlusion state.
- */
- @Nullable
- private Task getRootTaskForControllingOccluding(DisplayContent display) {
- return display.getRootTask(task ->
- task != null && task.isFocusableAndVisible() && !task.inPinnedWindowingMode());
- }
-
- void dumpStatus(PrintWriter pw, String prefix) {
- final StringBuilder sb = new StringBuilder();
- sb.append(prefix);
- sb.append(" KeyguardShowing=")
- .append(mKeyguardShowing)
- .append(" AodShowing=")
- .append(mAodShowing)
- .append(" KeyguardGoingAway=")
- .append(mKeyguardGoingAway)
- .append(" DismissalRequested=")
- .append(mDismissalRequested)
- .append(" Occluded=")
- .append(mOccluded)
- .append(" DismissingKeyguardActivity=")
- .append(mDismissingKeyguardActivity)
- .append(" TurnScreenOnActivity=")
- .append(mTopTurnScreenOnActivity)
- .append(" at display=")
- .append(mDisplayId);
- pw.println(sb.toString());
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId);
- proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING, mKeyguardShowing);
- proto.write(KeyguardPerDisplayProto.AOD_SHOWING, mAodShowing);
- proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, mOccluded);
- proto.write(KeyguardPerDisplayProto.KEYGUARD_GOING_AWAY, mKeyguardGoingAway);
- proto.end(token);
}
}
- void dump(PrintWriter pw, String prefix) {
- final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
- pw.println(prefix + "KeyguardController:");
- pw.println(prefix + " mKeyguardShowing=" + default_state.mKeyguardShowing);
- pw.println(prefix + " mAodShowing=" + default_state.mAodShowing);
- pw.println(prefix + " mKeyguardGoingAway=" + default_state.mKeyguardGoingAway);
- dumpDisplayStates(pw, prefix);
- pw.println(prefix + " mDismissalRequested=" + default_state.mDismissalRequested);
- pw.println();
+ /**
+ * Update the Keyguard showing state.
+ *
+ * @deprecated Use {@link #setKeyguardState(int, int)} instead. See b/242851358
+ */
+ @Deprecated
+ void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) {
+ final DisplayState state = getDisplayState(displayId);
+ EventLogTags.writeWmSetKeyguardShown(
+ displayId,
+ keyguardShowing ? 1 : 0,
+ aodShowing ? 1 : 0,
+ state.isIn(KEYGUARD_STATE_GOING_AWAY) ? 1 : 0,
+ "setKeyguardShown");
+ setKeyguardState(displayId, convertToState(displayId, keyguardShowing, aodShowing));
+ }
+
+ /**
+ * Set keyguard state.
+ */
+ private void setKeyguardState(int displayId, @KeyguardState int newState) {
+ if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) {
+ Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId);
+ return;
+ }
+ if (newState != KEYGUARD_STATE_LOCKSCREEN_SHOWN
+ && newState != KEYGUARD_STATE_AOD_SHOWN
+ && newState != KEYGUARD_STATE_OFF
+ && newState != KEYGUARD_STATE_GOING_AWAY) {
+ Slog.i(TAG, "Invalid state is requested: displayId=" + displayId
+ + ", state=" + keyguardStateToString(newState)
+ + ", stack=" + Debug.getCallers(30));
+ return;
+ }
+ if (isKeyguardLocked(displayId) && newState == KEYGUARD_STATE_OFF) {
+ newState = KEYGUARD_STATE_GOING_AWAY;
+ }
+
+ final DisplayState state = getDisplayState(displayId);
+ // SysUI requests to show LOCKSCREEN, but the keyguard is already occluded. Ignore the
+ // requests.
+ if (state.isIn(KEYGUARD_STATE_OCCLUDED)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) {
+ Slog.w(TAG, "Ignore setKeyguardState request: OCCLUDE -> LOCK_SCREEN_SHOWN");
+ return;
+ }
+ // SysUI requests to show AOD_SHOWN again. This can happen when SysUI still uses the old
+ // API and enables AOD first, then lock screen, i.e. #setLockScreenShown(false, true), then
+ // #setLockScreenShown(true, true)
+ if (state.isIn(KEYGUARD_STATE_AOD_SHOWN)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_AOD_SHOWN)) {
+ Slog.w(TAG, "Ignore setKeyguardState request: AOD_SHOWN -> AOD_SHOWN");
+ return;
+ }
+ if (state.isIn(KEYGUARD_STATE_OFF)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_GOING_AWAY)) {
+ Slog.w(TAG, "Ignore setKeyguardState request: OFF -> GOING_AWAY");
+ return;
+ }
+ if (state.isIn(KEYGUARD_STATE_AOD_SHOWN)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) {
+ ActivityRecord top = getTopNonFinishingActivity(displayId);
+ if (canOcclude(top)) {
+ newState = isTopActivityDreaming(displayId) ? KEYGUARD_STATE_DREAMING
+ : KEYGUARD_STATE_OCCLUDED;
+ }
+ }
+ state.setKeyguardState(newState);
+ }
+
+ /**
+ * Called when Keyguard is going away.
+ *
+ * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
+ * etc.
+ *
+ * @deprecated Use {@link #setKeyguardState(int, int)}
+ */
+ void keyguardGoingAway(int displayId, int flags) {
+ // TODO(b/242851358): Remove IActivityTaskManagerService#keyguardGoingAway and SysUI should
+ // request the state change via #setKeyguardState.
+ final DisplayState state = getDisplayState(displayId);
+ EventLogTags.writeWmSetKeyguardShown(
+ displayId,
+ state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN) ? 1 : 0,
+ state.isIn(KEYGUARD_STATE_AOD_SHOWN) ? 1 : 0,
+ 1 /* keyguardGoingAway */,
+ "keyguardGoingAway");
+ setKeyguardState(displayId, KEYGUARD_STATE_GOING_AWAY);
+ }
+
+ /**
+ * Makes sure to update lockscreen state if needed before completing set all visibility
+ * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}).
+ */
+ void updateVisibility() {
+ for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
+ displayNdx >= 0; displayNdx--) {
+ final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
+ if (display.isRemoving() || display.isRemoved()) continue;
+ final DisplayState state = getDisplayState(display.mDisplayId);
+ state.updateVisibility();
+ }
+ }
+
+ void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
+ boolean ok;
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ ok = false;
+ } else {
+ final DisplayState state = getDisplayState(r.getDisplayId());
+ ok = state.dismissKeyguard(r, callback, message);
+ }
+
+ if (!ok) {
+ try {
+ callback.onDismissError();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call callback", e);
+ }
+ }
+ }
+
+ private boolean isKeyguardSecure() {
+ return mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
+ }
+
+ /**
+ * @return true if Keyguard can be currently dismissed without entering credentials.
+ */
+ private boolean canDismissKeyguard() {
+ return mWindowManager.mPolicy.isKeyguardTrustedLw() || !isKeyguardSecure();
+ }
+
+ private boolean canOcclude(@Nullable ActivityRecord r) {
+ if (r == null) {
+ return false;
+ }
+ // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
+ if (r.getDisplayId() != DEFAULT_DISPLAY) {
+ final DisplayContent dc = r.getDisplayContent();
+ if (dc != null && dc.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
+ return true;
+ }
+ }
+ // FLAG_DISMISS_KEYGUARD activity
+ // Insecure: Treat as FLAG_SHOW_WHEN_LOCKED
+ // Trusted: Actually dismiss Keyguard.
+ // Secure: Show bouncer.
+ return r.canShowWhenLocked() || (r.containsDismissKeyguardWindow() && !isKeyguardSecure());
+ }
+
+ private boolean isTopActivityDreaming(int displayId) {
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ final ActivityRecord top = getTopNonFinishingActivity(displayId);
+ return dc.getDisplayPolicy().isShowingDreamLw()
+ && top != null && top.getActivityType() == ACTIVITY_TYPE_DREAM;
+ }
+
+ @Nullable private ActivityRecord getTopNonFinishingActivity(int displayId) {
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ final Task rootTask = dc == null ? null : dc.getRootTask(t ->
+ t != null && t.isFocusableAndVisible() && !t.inPinnedWindowingMode());
+ return rootTask != null ? rootTask.getTopNonFinishingActivity() : null;
+ }
+
+ private DisplayState getDisplayState(int displayId) {
+ DisplayState state = mDisplayStates.get(displayId);
+ if (state == null) {
+ state = new DisplayState(displayId, mServiceDelegate);
+ mDisplayStates.append(displayId, state);
+ }
+ return state;
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
+ final DisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
final long token = proto.start(fieldId);
- proto.write(AOD_SHOWING, default_state.mAodShowing);
- proto.write(KEYGUARD_SHOWING, default_state.mKeyguardShowing);
- proto.write(KEYGUARD_GOING_AWAY, default_state.mKeyguardGoingAway);
+ proto.write(AOD_SHOWING, default_state.isIn(KEYGUARD_STATE_AOD_SHOWN));
+ proto.write(KEYGUARD_SHOWING, default_state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN));
writeDisplayStatesToProto(proto, KEYGUARD_PER_DISPLAY);
proto.end(token);
}
- private void dumpDisplayStates(PrintWriter pw, String prefix) {
- for (int i = 0; i < mDisplayStates.size(); i++) {
- mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
- }
- }
-
private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
for (int i = 0; i < mDisplayStates.size(); i++) {
mDisplayStates.valueAt(i).dumpDebug(proto, fieldId);
}
}
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("KeyguardController:");
+ for (int i = 0; i < mDisplayStates.size(); i++) {
+ mDisplayStates.valueAt(i).dump(pw, prefix);
+ }
+ pw.println();
+ }
+
+ /**
+ * Interface for {@link DisplayState} to access non-local information.
+ * <p>
+ * Keep this interface as small as possible, and don't let {@link DisplayState} access arbitrary
+ * large classes such as ActivityTaskSupervisor, which makes managing dependency complicated.
+ */
+ private final class ServiceDelegate {
+ boolean isKeyguardSecure() {
+ return KeyguardController.this.isKeyguardSecure();
+ }
+
+ boolean canOcclude(@Nullable ActivityRecord r) {
+ return KeyguardController.this.canOcclude(r);
+ }
+
+ boolean canDismissKeyguard() {
+ return KeyguardController.this.canDismissKeyguard();
+ }
+
+ boolean isDeviceInteractive() {
+ return mService.mWindowManager.mPowerManager.isInteractive();
+ }
+
+ void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) {
+ mWindowManager.dismissKeyguard(callback, message);
+ }
+
+ @Nullable
+ ActivityRecord getTopNonFinishingActivity(int displayId) {
+ return KeyguardController.this.getTopNonFinishingActivity(displayId);
+ }
+
+ boolean isTopActivityDreaming(int displayId) {
+ return KeyguardController.this.isTopActivityDreaming(displayId);
+ }
+
+ void wakeUp(String reason) {
+ mTaskSupervisor.wakeUp(reason);
+ }
+
+ void forceSyncOccludedStatus(boolean occluded) {
+ if (DEBUG) {
+ Slog.d(TAG, "forceSyncOccludedStatus: occluded=" + occluded);
+ }
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+ mWindowManager.mPolicy.applyKeyguardOcclusionChange(true /* notify */);
+ }
+
+ void snapshotForSleeping(int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ mWindowManager.mTaskSnapshotController.snapshotForSleeping(displayId);
+ }
+ }
+
+ void notifyKeyguardOccludeChanged(boolean occluded) {
+ // TODO: This updates status of KeyguardDelegate. Once we delete occlude status from
+ // KeyguardDelegate, we should remove WindowManagerPolicy#onKeyguardOccludedChangedLw.
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+ }
+
+ void collect(@NonNull WindowContainer wc) {
+ mService.getTransitionController().collect(wc);
+ }
+
+ void requestTransitionIfNeeded(int displayId, @WindowManager.TransitionType int transit,
+ @WindowManager.TransitionFlags int flags) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "requestTransitionIfNeeded: display=" + displayId + ", transit="
+ + transitTypeToString(transit));
+ }
+
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ if (dc == null) {
+ Slog.e(TAG, "No DisplayContent exists: displayId=" + displayId);
+ return;
+ }
+
+ if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+ dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, flags);
+ // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
+ // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard
+ // going away.
+ mService.getTransitionController().requestTransitionIfNeeded(
+ TRANSIT_TO_BACK, flags, null /* trigger */, dc);
+ } else {
+ dc.requestTransitionAndLegacyPrepare(transit, flags);
+ }
+ }
+
+ void acquireSleepToken(int displayId, boolean ensureActivitiesVisible) {
+ mSleepTokenAcquirer.acquire(displayId);
+ if (ensureActivitiesVisible) {
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ }
+ }
+
+ void releaseSleepToken(int displayId, boolean resumeTopActivities) {
+ mSleepTokenAcquirer.release(displayId);
+ if (resumeTopActivities) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.addStartingWindowsForVisibleActivities();
+
+ }
+ }
+
+ void deferWindowLayout() {
+ mService.deferWindowLayout();
+ }
+
+ void continueWindowLayout() {
+ mService.continueWindowLayout();
+ }
+
+ void executeAppTransition() {
+ mWindowManager.executeAppTransition();
+ }
+
+ private void updateDeferTransitionForAod(boolean waiting) {
+ KeyguardController.this.updateDeferTransitionForAod(waiting);
+ }
+
+ private void setWakeTransitionReady() {
+ if (mService.getTransitionController().getCollectingTransitionType() == TRANSIT_WAKE) {
+ mService.getTransitionController().setReady(
+ mRootWindowContainer.getDefaultDisplay());
+ }
+ }
+
+ void requestLayoutRedoWallpaper(int displayId) {
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ if (!dc.isSleeping() && dc.mWallpaperController.getWallpaperTarget() != null) {
+ dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ }
+ }
+ };
+
+ private static class KeyguardDisplayStateMachine extends StateMachine {
+ static final int EVENT_DISMISS_KEYGUARD_ACTIVITY = 1;
+ static final int EVENT_SHOW_WHEN_LOCKED_ACTIVITY = 2;
+ static final int EVENT_CHECK_KEYGUARD_VISIBILITY = 3;
+ static final int EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD = 4;
+ static final int EVENT_LAYOUT_CHANGES = 5;
+ static final int EVENT_DUMP = 6;
+ static final int EVENT_DISMISS_KEYGUARD_API = 7;
+ static final int EVENT_TURN_SCREEN_ON_ACTIVITY = 8;
+ final int mDisplayId;
+
+ static final class CheckKeyguardVisibilityParam {
+ boolean mRet;
+ @NonNull final ActivityRecord mActivity;
+
+ CheckKeyguardVisibilityParam(@NonNull ActivityRecord activity) {
+ mActivity = activity;
+ }
+ }
+
+ static final class DismissKeyguardParam {
+ boolean mRet;
+ @NonNull final ActivityRecord mActivity;
+ @Nullable final IKeyguardDismissCallback mCallback;
+ @Nullable final CharSequence mMessage;
+
+ DismissKeyguardParam(@NonNull ActivityRecord activity,
+ @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+ mActivity = activity;
+ mCallback = callback;
+ mMessage = message;
+ }
+ }
+
+ static final class TopActivityOccludesKeyguardParam {
+ boolean mRet;
+ @NonNull final ActivityRecord mActivity;
+
+ TopActivityOccludesKeyguardParam(@NonNull ActivityRecord activity) {
+ mActivity = activity;
+ }
+ }
+
+ static final class DumpParam {
+ ArrayList<String> mRet = new ArrayList<>();
+ String mPrefix;
+
+ DumpParam(@NonNull String prefix) {
+ mPrefix = prefix;
+ }
+ }
+
+ KeyguardDisplayStateMachine(int displayId, @KeyguardState int initialState) {
+ super(initialState);
+ mDisplayId = displayId;
+ }
+
+ @Nullable
+ @Override
+ public Handler addStateHandler(int state, Handler handler) {
+ Handler prevHandler = super.addStateHandler(state, handler);
+ if (prevHandler != null) {
+ throw new IllegalStateException(
+ "Duplicate state handler registration: display=" + mDisplayId
+ + ", state=" + state);
+ }
+ return null;
+ }
+
+ @Override
+ public void transit(@KeyguardState int newState) {
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ ");
+ for (Command cmd : getCommands()) {
+ sb.append(cmd);
+ sb.append(' ');
+ }
+ sb.append(" ]");
+ Slog.d(TAG, "State change: display=" + mDisplayId
+ + ", current=" + keyguardStateToString(getCurrentState())
+ + ", lastRequested=" + keyguardStateToString(getState())
+ + ", newState=" + keyguardStateToString(newState)
+ + ", command=" + sb
+ + ", stack=" + Debug.getCallers(30));
+ }
+ super.transit(newState);
+ }
+
+ @Override
+ public void enter(@KeyguardState int state) {
+ if (DEBUG) {
+ Slog.d(TAG, "enter: display=" + mDisplayId + ", state="
+ + keyguardStateToString(state));
+ }
+ super.enter(state);
+ }
+
+ @Override
+ public void exit(@KeyguardState int state) {
+ if (DEBUG) {
+ Slog.d(TAG, "exit: display=" + mDisplayId + ", state="
+ + keyguardStateToString(state));
+ }
+ super.exit(state);
+ }
+
+ void handleDismissKeyguardActivity() {
+ handle(EVENT_DISMISS_KEYGUARD_ACTIVITY, null /* param */);
+ }
+
+ void handleTurnScreenOnActivity() {
+ handle(EVENT_TURN_SCREEN_ON_ACTIVITY, null /* param */);
+ }
+
+ boolean handleDismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+ DismissKeyguardParam param = new DismissKeyguardParam(r, callback, message);
+ handle(EVENT_DISMISS_KEYGUARD_API, param);
+ return param.mRet;
+ }
+
+ void handleShowWhenLockedActivity() {
+ handle(EVENT_SHOW_WHEN_LOCKED_ACTIVITY, null /* param */);
+ }
+
+ void handleLayoutChanges() {
+ handle(EVENT_LAYOUT_CHANGES, null /* param */);
+ }
+
+ boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ final CheckKeyguardVisibilityParam param = new CheckKeyguardVisibilityParam(r);
+ handle(EVENT_CHECK_KEYGUARD_VISIBILITY, param);
+ return param.mRet;
+ }
+
+ boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) {
+ final TopActivityOccludesKeyguardParam param = new TopActivityOccludesKeyguardParam(r);
+ handle(EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD, param);
+ return param.mRet;
+ }
+
+ ArrayList<String> handleDump(String prefix) {
+ final DumpParam param = new DumpParam(prefix);
+ handle(EVENT_DUMP, param);
+ return param.mRet;
+ }
+ }
+
+ /**
+ * Helper class for implementing handler in type-safe way.
+ */
+ private abstract static class Handler implements StateMachine.Handler {
+ @Override
+ public final boolean handle(int event, @Nullable Object param) {
+ switch (event) {
+ case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_ACTIVITY:
+ return handleDismissKeyguardActivity();
+ case KeyguardDisplayStateMachine.EVENT_TURN_SCREEN_ON_ACTIVITY:
+ return handleTurnScreenOnActivity();
+ case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_API: {
+ final KeyguardDisplayStateMachine.DismissKeyguardParam typedParam =
+ (KeyguardDisplayStateMachine.DismissKeyguardParam) param;
+ Optional<Boolean> ret = handleDismissKeyguard(typedParam.mActivity,
+ typedParam.mCallback, typedParam.mMessage);
+ if (ret.isPresent()) {
+ typedParam.mRet = ret.get();
+ return true;
+ }
+ return false;
+ }
+ case KeyguardDisplayStateMachine.EVENT_SHOW_WHEN_LOCKED_ACTIVITY:
+ return handleShowWhenLockedActivity();
+ case KeyguardDisplayStateMachine.EVENT_CHECK_KEYGUARD_VISIBILITY: {
+ final KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam typedParam =
+ (KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam) param;
+ Optional<Boolean> ret = checkKeyguardVisibility(typedParam.mActivity);
+ if (ret.isPresent()) {
+ typedParam.mRet = ret.get();
+ return true;
+ }
+ return false;
+ }
+ case KeyguardDisplayStateMachine.EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD: {
+ final KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam typedParam =
+ (KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam) param;
+ Optional<Boolean> ret = topActivityOccludesKeyguardParam(typedParam.mActivity);
+ if (ret.isPresent()) {
+ typedParam.mRet = ret.get();
+ return true;
+ }
+ return false;
+ }
+ case KeyguardDisplayStateMachine.EVENT_LAYOUT_CHANGES:
+ return handleLayoutChanges();
+ case KeyguardDisplayStateMachine.EVENT_DUMP:
+ final KeyguardDisplayStateMachine.DumpParam typedParam =
+ (KeyguardDisplayStateMachine.DumpParam) param;
+ String dumpInfo = handleDump(typedParam.mPrefix);
+ if (dumpInfo != null) {
+ typedParam.mRet.add(dumpInfo);
+ }
+ // keep collecting information for dump up to top status.
+ return false;
+ default:
+ Slog.e(TAG, "Handler.handle(): Unknown event(" + event + ")");
+ return false;
+ }
+ }
+
+ Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+ return Optional.empty();
+ }
+
+ Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+ return Optional.empty();
+ }
+
+ /**
+ * Handle flags in the activity which request to dismiss the keyguard.
+ *
+ * @see ActivityOptions#setDismissKeyguard()
+ * @see WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD
+ */
+ boolean handleDismissKeyguardActivity() {
+ return false;
+ }
+
+ /**
+ * Handle flags in the activity which request to turn the screen on. This must be called
+ * after dismiss keyguard flag is handled.
+ */
+ boolean handleTurnScreenOnActivity() {
+ return false;
+ }
+ /**
+ * Handle flags in the activity which decides if the activity can be shown on top of the
+ * keyguard.
+ *
+ * @see android.app.Activity#setShowWhenLocked(boolean)
+ * @see android.app.Activity#setInheritShowWhenLocked(boolean)
+ */
+ boolean handleShowWhenLockedActivity() {
+ return false;
+ }
+
+ /**
+ * Request relayout if necessary.
+ */
+ boolean handleLayoutChanges() {
+ return false;
+ }
+
+ /**
+ * Called when the activity requests to dismiss the keyguard via KeyguardManager APIs.
+ *
+ * @param r The activity which requested to dismiss the keyguard.
+ * @return Present if the state handles, delegate to its parent state otherwise. When the
+ * value is present, the value is {@code true} if the keyguard dismiss request is
+ * processed, {@code false} otherwise.
+ */
+ Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+ return Optional.empty();
+ }
+
+ @Nullable String handleDump(@NonNull String prefix) {
+ return null;
+ }
+ }
+
+ private static class DisplayState {
+ private final int mDisplayId;
+ @NonNull private final ServiceDelegate mServiceDelegate;
+ private final KeyguardDisplayStateMachine mStateMachine;
+
+ // TODO: Set watchdog timer to sync mLastNotifiedOccludedState == isIn(OCCLUDED)
+ private boolean mLastNotifiedOccludedState = false;
+
+ // Top activity which has a window with FLAG_DISMISS_KEYGUARD flag. Valid only when the
+ // current state is KEYGUARD_STATE_ON or one of its sub states.
+ @Nullable private ActivityRecord mDismissingKeyguardActivity;
+
+ // KeyguardController has requested to dismiss keyguard via IWindowManager#dismissKeyguard.
+ // Reset this to false again, once the KeyguardController status is updated.
+ private boolean mDismissalRequested = false;
+
+ DisplayState(int displayId, @NonNull ServiceDelegate serviceDelegate) {
+ mDisplayId = displayId;
+ mServiceDelegate = serviceDelegate;
+ mStateMachine = new KeyguardDisplayStateMachine(displayId, KEYGUARD_STATE_OFF);
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_ROOT, new Handler() {
+ @Override
+ Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+ return Optional.of(false);
+ }
+
+ @Override
+ Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+ return Optional.of(false);
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_OFF, new Handler() {
+ @Override
+ public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ return Optional.of(true);
+ }
+
+ @Override
+ Optional<Boolean> handleDismissKeyguard(
+ @NonNull ActivityRecord r, @Nullable IKeyguardDismissCallback callback,
+ @Nullable CharSequence message) {
+ // Keyguard is not shown, so we don't handle the request to dismiss the
+ // keyguard.
+ return Optional.of(false);
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_GOING_AWAY, new Handler() {
+ @Override
+ public void enter() {
+ mServiceDelegate.deferWindowLayout();
+ try {
+ mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+ TRANSIT_KEYGUARD_GOING_AWAY,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER);
+ // Some stack visibility might change (e.g. docked stack)
+ mServiceDelegate.releaseSleepToken(mDisplayId,
+ true /* resumeTopActivities */);
+ mServiceDelegate.executeAppTransition();
+ } finally {
+ mServiceDelegate.continueWindowLayout();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_ON, new Handler() {
+ public boolean handleDismissKeyguardActivity() {
+ final ActivityRecord lastDismissingKeyguardActivity =
+ mDismissingKeyguardActivity;
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ mDismissingKeyguardActivity =
+ (top != null && top.containsDismissKeyguardWindow()) ? top : null;
+ if (lastDismissingKeyguardActivity != mDismissingKeyguardActivity
+ && mDismissingKeyguardActivity != null
+ && mServiceDelegate.isKeyguardSecure()) {
+ // We only allow dismissing Keyguard via the flag when Keyguard is secure
+ // for legacy reasons, because that's how apps used to dismiss Keyguard in
+ // the secure case. In the insecure case, we actually show it on top of the
+ // lockscreen. See #canShowWhileOccluded.
+ mDismissalRequested = true;
+ mServiceDelegate.dismissKeyguard(null, null);
+ }
+ return true;
+ }
+
+ @Override
+ Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback,
+ @Nullable CharSequence message) {
+ if (!r.visibleIgnoringKeyguard) {
+ return Optional.of(false);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Activity requesting to dismiss Keyguard: " + r);
+ }
+ // If the client has requested to dismiss the keyguard and the Activity has the
+ // flag to turn the screen on, wakeup the screen if it's the top Activity.
+ // Note that it's possible that the client requests to dismiss the keyguard
+ // before the activity adds a window. In this case the flag set on the window
+ // is not yet visible from ActivityRecord, so we need to check the flag again
+ // when the activity adds a window later. See #handleTurnScreenOnActivity().
+ if (r.getTurnScreenOnFlag() && r.isTopRunningActivity()) {
+ mServiceDelegate.wakeUp("ON/handleDismissKeyguard");
+ r.setCurrentLaunchCanTurnScreenOn(false);
+ }
+ mDismissalRequested = true;
+ mServiceDelegate.dismissKeyguard(callback, message);
+ return Optional.of(true);
+ }
+
+ @Override
+ public void enter() {
+ // Update the task snapshot if the screen will not be turned off. To make sure
+ // that the unlocking animation can animate consistent content.
+ mServiceDelegate.snapshotForSleeping(mDisplayId);
+ }
+
+ @Nullable
+ @Override
+ String handleDump(@NonNull String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append(" mDismissingKeyguardActivity=")
+ .append(mDismissingKeyguardActivity)
+ .append("\n");
+ return sb.toString();
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_OCCLUDED, new Handler() {
+ ActivityRecord mTopOccludesActivity;
+
+ @Override
+ Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+ return Optional.of(mServiceDelegate.canOcclude(activity));
+ }
+
+ @Override
+ public boolean handleShowWhenLockedActivity() {
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top)
+ ? top : null;
+ if (mTopOccludesActivity == topOccludesActivity) {
+ return true;
+ }
+ // Launch SHOW_WHEN_LOCKED or INHERIT_SHOW_WHEN_LOCKED activity on top of an
+ // occluding activity.
+ mTopOccludesActivity = topOccludesActivity;
+ if (mServiceDelegate.isTopActivityDreaming(mDisplayId)) {
+ // Dream activity is launched on top of the previous SHOW_WHEN_LOCKED
+ // activity.
+ setKeyguardState(KEYGUARD_STATE_DREAMING);
+ } else if (topOccludesActivity == null) {
+ // SHOW_WHEN_LOCKED activity finishes.
+ setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
+ }
+ return true;
+ }
+
+ @Override
+ boolean handleLayoutChanges() {
+ // The occluding activity may be translucent or not fill screen. Then let
+ // wallpaper to check whether it should set itself as target to avoid blank
+ // background.
+ if (!mTopOccludesActivity.fillsParent()) {
+ mServiceDelegate.requestLayoutRedoWallpaper(mDisplayId);
+ }
+ return true;
+ }
+
+ @Override
+ Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+ return Optional.of(mTopOccludesActivity == r);
+ }
+
+ @Override
+ public void enter() {
+ mTopOccludesActivity = mServiceDelegate.getTopNonFinishingActivity(mDisplayId);
+ if (!mServiceDelegate.canOcclude(mTopOccludesActivity)) {
+ Slog.e(TAG, "enter(OCCLUDE): no occluding activity");
+ setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "handleOccludedChanged: display=" + mDisplayId
+ + ", topActivity=" + mTopOccludesActivity);
+ }
+ // Collect the participates for shell transition, so that transition won't
+ // happen too early since the transition was set ready.
+ mServiceDelegate.collect(mTopOccludesActivity);
+ // TODO(b/113840485): Handle app transition for individual display, and apply
+ // occluded state change to secondary displays. For now, only default display
+ // fully supports occluded change. Other displays only updates keyguard sleep
+ // token on that display.
+ if (mDisplayId != DEFAULT_DISPLAY) {
+ mServiceDelegate.releaseSleepToken(mDisplayId,
+ false /* resumeTopActivities */);
+ return;
+ }
+
+ if (mTopOccludesActivity.getTurnScreenOnFlag()
+ && mTopOccludesActivity.currentLaunchCanTurnScreenOn()
+ && !mServiceDelegate.isDeviceInteractive()) {
+ mServiceDelegate.wakeUp("OCCLUDE/enter");
+ mTopOccludesActivity.setCurrentLaunchCanTurnScreenOn(false);
+ }
+
+ mServiceDelegate.notifyKeyguardOccludeChanged(true /* occluded */);
+ mServiceDelegate.deferWindowLayout();
+ try {
+ mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+ TRANSIT_KEYGUARD_OCCLUDE, 0 /* flags */);
+ mServiceDelegate.releaseSleepToken(mDisplayId,
+ false /* resumeTopActivities */);
+ mServiceDelegate.executeAppTransition();
+ } finally {
+ mServiceDelegate.continueWindowLayout();
+ }
+ // Dismiss freeform windowing mode
+ final Task currentTaskControllingOcclusion = mTopOccludesActivity.getRootTask();
+ if (currentTaskControllingOcclusion != null
+ && currentTaskControllingOcclusion.inFreeformWindowingMode()) {
+ currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ @Override
+ public void exit() {
+ mTopOccludesActivity = null;
+ if (DEBUG) {
+ Slog.d(TAG, "handleOccludedChanged: topActivity=" + null);
+ }
+ // TODO(b/113840485): Handle app transition for individual display, and apply
+ // occluded state change to secondary displays.
+ // For now, only default display fully supports occluded change. Other displays
+ // only updates keyguard sleep token on that display.
+ if (mDisplayId != DEFAULT_DISPLAY) {
+ mServiceDelegate.acquireSleepToken(
+ mDisplayId, false /* ensureActivitiesVisible */);
+ return;
+ }
+
+ mServiceDelegate.notifyKeyguardOccludeChanged(false /* occluded */);
+ mServiceDelegate.deferWindowLayout();
+ try {
+ mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+ TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
+ mServiceDelegate.acquireSleepToken(
+ mDisplayId, false /* ensureActivitiesVisible */);
+ mServiceDelegate.executeAppTransition();
+ } finally {
+ mServiceDelegate.continueWindowLayout();
+ }
+ }
+
+ @Nullable
+ @Override
+ String handleDump(@NonNull String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append(" mTopOccludesActivity=")
+ .append(mTopOccludesActivity)
+ .append("\n");
+ return sb.toString();
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_KEYGUARD_TOP, new Handler() {
+ @Override
+ public boolean handleDismissKeyguardActivity() {
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ if (top != null && top.mDismissKeyguard) {
+ // Top activity has been launched with ActivityOptions#setDismissKeyguard.
+ // Authentication has already been passed, so we can turn off the keyguard
+ // immediately.
+ top.mDismissKeyguard = false;
+ setKeyguardState(KEYGUARD_STATE_GOING_AWAY);
+ // Collect the participates for shell transition, so that transition won't
+ // happen too early since the transition was set ready.
+ mServiceDelegate.collect(top);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void enter() {
+ mServiceDelegate.acquireSleepToken(mDisplayId,
+ true /* ensureActivitiesVisible */);
+ InputMethodManagerInternal.get().updateImeWindowStatus(
+ false /* disableImeIcon */);
+ mServiceDelegate.setWakeTransitionReady();
+ }
+
+ @Override
+ public void exit() {
+ // Sleep token is released in enter() action in other states, since we need
+ // to call requestTransition() before updating visibility of the activities.
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_LOCKSCREEN_SHOWN, new Handler() {
+ @Override
+ public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ // If lock screen is showing, nothing is visible, except if we are able to
+ // dismiss Keyguard right away. This isn't allowed if r is already the
+ // dismissing activity, in which case we don't allow it to repeatedly
+ // dismiss Keyguard.
+ return Optional.of(r.containsDismissKeyguardWindow()
+ && mServiceDelegate.canDismissKeyguard()
+ && (mDismissalRequested
+ || (r.canShowWhenLocked() && mDismissingKeyguardActivity != r)));
+ }
+
+ @Override
+ public boolean handleShowWhenLockedActivity() {
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top)
+ ? top : null;
+ if (topOccludesActivity != null) {
+ setKeyguardState(mServiceDelegate.isTopActivityDreaming(mDisplayId)
+ ? KEYGUARD_STATE_DREAMING : KEYGUARD_STATE_OCCLUDED);
+ }
+ return true;
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_AOD_SHOWN, new Handler() {
+ // Top activity which has FLAG_TURN_SCREEN_ON flag.
+ @Nullable private ActivityRecord mTopTurnScreenOnActivity;
+
+ @Override
+ public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ return Optional.of(false);
+ }
+
+ @Override
+ boolean handleTurnScreenOnActivity() {
+ final ActivityRecord lastTopTurnScreenOnActivity = mTopTurnScreenOnActivity;
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ mTopTurnScreenOnActivity = (top != null && top.getTurnScreenOnFlag()
+ && top.currentLaunchCanTurnScreenOn()) ? top : null;
+ if (mTopTurnScreenOnActivity != lastTopTurnScreenOnActivity
+ && mTopTurnScreenOnActivity != null
+ && !mServiceDelegate.isDeviceInteractive()
+ && mDismissalRequested) {
+ mServiceDelegate.wakeUp("AOD_SHOWN/handleTurnScreenOnActivity");
+ mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
+ }
+ return true;
+ }
+
+ @Override
+ public void enter() {
+ if (mLastNotifiedOccludedState) {
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ mServiceDelegate.forceSyncOccludedStatus(false);
+ }
+ commitOccludedStatus(false);
+ }
+ }
+
+ @Override
+ public void exit() {
+ mServiceDelegate.updateDeferTransitionForAod(false /* waiting */);
+ }
+
+ @Nullable
+ @Override
+ String handleDump(@NonNull String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append(" mTopTurnScreenOnActivity=")
+ .append(mTopTurnScreenOnActivity)
+ .append("\n");
+ return sb.toString();
+ }
+ });
+ }
+
+ void onRemoved() {
+ mServiceDelegate.releaseSleepToken(mDisplayId, false /* resumeTopActivities */);
+ }
+
+ void updateVisibility() {
+ mStateMachine.handleDismissKeyguardActivity();
+ mStateMachine.handleTurnScreenOnActivity();
+ mStateMachine.handleShowWhenLockedActivity();
+ mStateMachine.handleLayoutChanges();
+ }
+
+ boolean dismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback,
+ @Nullable CharSequence message) {
+ return mStateMachine.handleDismissKeyguard(r, callback, message);
+ }
+
+ void commitOccludedStatus(boolean occluded) {
+ mLastNotifiedOccludedState = occluded;
+ }
+
+ void setKeyguardState(@KeyguardState int newState) {
+ mDismissalRequested = false;
+ mStateMachine.transit(newState);
+ }
+
+ boolean isKeyguardTop() {
+ return mStateMachine.isIn(KEYGUARD_STATE_KEYGUARD_TOP);
+ }
+
+ boolean isIn(@KeyguardState int category) {
+ return mStateMachine.isIn(category);
+ }
+
+ boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) {
+ return mStateMachine.topActivityOccludesKeyguard(r);
+ }
+
+ boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ return mStateMachine.checkKeyguardVisibility(r);
+ }
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId);
+ proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING,
+ isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN));
+ proto.write(KeyguardPerDisplayProto.AOD_SHOWING, isIn(KEYGUARD_STATE_AOD_SHOWN));
+ proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, isIn(KEYGUARD_STATE_OCCLUDED));
+ proto.end(token);
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append("* display=")
+ .append(mDisplayId)
+ .append("\n");
+ sb.append(prefix)
+ .append(" state=")
+ .append(keyguardStateToString(mStateMachine.getState()))
+ .append("\n");
+ sb.append(prefix)
+ .append(" mLastNotifiedOccludedState=")
+ .append(mLastNotifiedOccludedState)
+ .append("\n");
+ sb.append(prefix)
+ .append(" mDismissalRequested=")
+ .append(mDismissalRequested)
+ .append("\n");
+ pw.print(sb.toString());
+
+ ArrayList<String> dumpInfo = mStateMachine.handleDump(prefix);
+ for (int i = dumpInfo.size() - 1; i >= 0; --i) {
+ pw.print(dumpInfo.get(i));
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 67e188f..c5a50ca 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -17,11 +17,21 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
+import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.isFixedOrientation;
+import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.screenOrientationToString;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -29,6 +39,8 @@
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
@@ -62,6 +74,9 @@
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.LetterboxConfiguration.letterboxBackgroundTypeToString;
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+
import android.annotation.Nullable;
import android.app.ActivityManager.TaskDescription;
import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -105,6 +120,40 @@
private final ActivityRecord mActivityRecord;
+ /**
+ * Taskbar expanded height. Used to determine when to crop an app window to display the
+ * rounded corners above the expanded taskbar.
+ */
+ private final float mExpandedTaskBarHeight;
+
+ // TODO(b/265576778): Cache other overrides as well.
+
+ // Corresponds to OVERRIDE_ANY_ORIENTATION
+ private final boolean mIsOverrideAnyOrientationEnabled;
+ // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
+ private final boolean mIsOverrideToPortraitOrientationEnabled;
+ // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
+ private final boolean mIsOverrideToNosensorOrientationEnabled;
+ // Corresponds to OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE
+ private final boolean mIsOverrideToReverseLandscapeOrientationEnabled;
+ // Corresponds to OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION
+ private final boolean mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled;
+
+ // Corresponds to OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION
+ private final boolean mIsOverrideCameraCompatDisableForceRotationEnabled;
+ // Corresponds to OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH
+ private final boolean mIsOverrideCameraCompatDisableRefreshEnabled;
+ // Corresponds to OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE
+ private final boolean mIsOverrideCameraCompatEnableRefreshViaPauseEnabled;
+
+ // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION
+ private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled;
+
+ @Nullable
+ private final Boolean mBooleanPropertyAllowOrientationOverride;
+ @Nullable
+ private final Boolean mBooleanPropertyAllowDisplayOrientationOverride;
+
/*
* WindowContainerListener responsible to make translucent activities inherit
* constraints from the first opaque activity beneath them. It's null for not
@@ -184,6 +233,38 @@
() -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
/* checkDeviceConfig */ true),
PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
+
+ mExpandedTaskBarHeight =
+ getResources().getDimensionPixelSize(R.dimen.taskbar_frame_height);
+
+ mBooleanPropertyAllowOrientationOverride =
+ readComponentProperty(packageManager, mActivityRecord.packageName,
+ /* gatingCondition */ null,
+ PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ mBooleanPropertyAllowDisplayOrientationOverride =
+ readComponentProperty(packageManager, mActivityRecord.packageName,
+ /* gatingCondition */ null,
+ PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE);
+
+ mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
+ mIsOverrideToPortraitOrientationEnabled =
+ isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT);
+ mIsOverrideToReverseLandscapeOrientationEnabled =
+ isCompatChangeEnabled(OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE);
+ mIsOverrideToNosensorOrientationEnabled =
+ isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR);
+ mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled =
+ isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION);
+
+ mIsOverrideCameraCompatDisableForceRotationEnabled =
+ isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION);
+ mIsOverrideCameraCompatDisableRefreshEnabled =
+ isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH);
+ mIsOverrideCameraCompatEnableRefreshViaPauseEnabled =
+ isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
+
+ mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled =
+ isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION);
}
/**
@@ -198,8 +279,8 @@
*/
@Nullable
private static Boolean readComponentProperty(PackageManager packageManager, String packageName,
- BooleanSupplier gatingCondition, String propertyName) {
- if (!gatingCondition.getAsBoolean()) {
+ @Nullable BooleanSupplier gatingCondition, String propertyName) {
+ if (gatingCondition != null && !gatingCondition.getAsBoolean()) {
return null;
}
try {
@@ -253,7 +334,7 @@
if (!shouldEnableWithOverrideAndProperty(
/* gatingCondition */ mLetterboxConfiguration
::isPolicyForIgnoringRequestedOrientationEnabled,
- OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION,
+ mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled,
mBooleanPropertyIgnoreRequestedOrientation)) {
return false;
}
@@ -299,6 +380,66 @@
}
/**
+ * Whether should fix display orientation to landscape natural orientation when a task is
+ * fullscreen and the display is ignoring orientation requests.
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Opt-out component property isn't enabled
+ * <li>Opt-in per-app override is enabled
+ * <li>Task is in fullscreen.
+ * <li>{@link DisplayContent#getIgnoreOrientationRequest} is enabled
+ * <li>Natural orientation of the display is landscape.
+ * </ul>
+ */
+ boolean shouldUseDisplayLandscapeNaturalOrientation() {
+ return shouldEnableWithOptInOverrideAndOptOutProperty(
+ /* gatingCondition */ () -> mActivityRecord.mDisplayContent != null
+ && mActivityRecord.getTask() != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()
+ && !mActivityRecord.getTask().inMultiWindowMode()
+ && mActivityRecord.mDisplayContent.getNaturalOrientation()
+ == ORIENTATION_LANDSCAPE,
+ mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled,
+ mBooleanPropertyAllowDisplayOrientationOverride);
+ }
+
+ @ScreenOrientation
+ int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
+ if (FALSE.equals(mBooleanPropertyAllowOrientationOverride)) {
+ return candidate;
+ }
+
+ if (mIsOverrideToReverseLandscapeOrientationEnabled
+ && (isFixedOrientationLandscape(candidate) || mIsOverrideAnyOrientationEnabled)) {
+ Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_REVERSE_LANDSCAPE));
+ return SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+ }
+
+ if (!mIsOverrideAnyOrientationEnabled && isFixedOrientation(candidate)) {
+ return candidate;
+ }
+
+ if (mIsOverrideToPortraitOrientationEnabled) {
+ Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT));
+ return SCREEN_ORIENTATION_PORTRAIT;
+ }
+
+ if (mIsOverrideToNosensorOrientationEnabled) {
+ Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_NOSENSOR));
+ return SCREEN_ORIENTATION_NOSENSOR;
+ }
+
+ return candidate;
+ }
+
+ /**
* Whether activity is eligible for activity "refresh" after camera compat force rotation
* treatment. See {@link DisplayRotationCompatPolicy} for context.
*
@@ -313,7 +454,7 @@
return shouldEnableWithOptOutOverrideAndProperty(
/* gatingCondition */ () -> mLetterboxConfiguration
.isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
- OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH,
+ mIsOverrideCameraCompatDisableRefreshEnabled,
mBooleanPropertyCameraCompatAllowRefresh);
}
@@ -335,7 +476,7 @@
return shouldEnableWithOverrideAndProperty(
/* gatingCondition */ () -> mLetterboxConfiguration
.isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
- OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE,
+ mIsOverrideCameraCompatEnableRefreshViaPauseEnabled,
mBooleanPropertyCameraCompatEnableRefreshViaPause);
}
@@ -354,15 +495,19 @@
return shouldEnableWithOptOutOverrideAndProperty(
/* gatingCondition */ () -> mLetterboxConfiguration
.isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
- OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION,
+ mIsOverrideCameraCompatDisableForceRotationEnabled,
mBooleanPropertyCameraCompatAllowForceRotation);
}
+ private boolean isCompatChangeEnabled(long overrideChangeId) {
+ return mActivityRecord.info.isChangeEnabled(overrideChangeId);
+ }
+
/**
* Returns {@code true} when the following conditions are met:
* <ul>
* <li>{@code gatingCondition} isn't {@code false}
- * <li>OEM didn't opt out with a {@code overrideChangeId} override
+ * <li>OEM didn't opt out with a per-app override
* <li>App developers didn't opt out with a component {@code property}
* </ul>
*
@@ -370,12 +515,30 @@
* disabled on per-app basis by OEMs or app developers.
*/
private boolean shouldEnableWithOptOutOverrideAndProperty(BooleanSupplier gatingCondition,
- long overrideChangeId, Boolean property) {
+ boolean isOverrideChangeEnabled, Boolean property) {
if (!gatingCondition.getAsBoolean()) {
return false;
}
- return !Boolean.FALSE.equals(property)
- && !mActivityRecord.info.isChangeEnabled(overrideChangeId);
+ return !FALSE.equals(property) && !isOverrideChangeEnabled;
+ }
+
+ /**
+ * Returns {@code true} when the following conditions are met:
+ * <ul>
+ * <li>{@code gatingCondition} isn't {@code false}
+ * <li>OEM did opt in with a per-app override
+ * <li>App developers didn't opt out with a component {@code property}
+ * </ul>
+ *
+ * <p>This is used for the treatments that are enabled based with the heuristic but can be
+ * disabled on per-app basis by OEMs or app developers.
+ */
+ private boolean shouldEnableWithOptInOverrideAndOptOutProperty(BooleanSupplier gatingCondition,
+ boolean isOverrideChangeEnabled, Boolean property) {
+ if (!gatingCondition.getAsBoolean()) {
+ return false;
+ }
+ return !FALSE.equals(property) && isOverrideChangeEnabled;
}
/**
@@ -384,21 +547,20 @@
* <li>{@code gatingCondition} isn't {@code false}
* <li>App developers didn't opt out with a component {@code property}
* <li>App developers opted in with a component {@code property} or an OEM opted in with a
- * component {@code property}
+ * per-app override
* </ul>
*
* <p>This is used for the treatments that are enabled only on per-app basis.
*/
private boolean shouldEnableWithOverrideAndProperty(BooleanSupplier gatingCondition,
- long overrideChangeId, Boolean property) {
+ boolean isOverrideChangeEnabled, Boolean property) {
if (!gatingCondition.getAsBoolean()) {
return false;
}
- if (Boolean.FALSE.equals(property)) {
+ if (FALSE.equals(property)) {
return false;
}
- return Boolean.TRUE.equals(property)
- || mActivityRecord.info.isChangeEnabled(overrideChangeId);
+ return TRUE.equals(property) || isOverrideChangeEnabled;
}
boolean hasWallpaperBackgroundForLetterbox() {
@@ -422,7 +584,7 @@
if (w == null) {
return;
}
- adjustBoundsForTaskbar(w, outBounds);
+ adjustBoundsIfNeeded(w, outBounds);
} else {
outBounds.setEmpty();
}
@@ -465,13 +627,13 @@
if (w == null || winHint != null && w != winHint) {
return;
}
- updateRoundedCorners(w);
+ updateRoundedCornersIfNeeded(w);
// If there is another main window that is not an application-starting window, we should
// update rounded corners for it as well, to avoid flickering rounded corners.
final WindowState nonStartingAppW = mActivityRecord.findMainWindow(
/* includeStartingApp= */ false);
if (nonStartingAppW != null && nonStartingAppW != w) {
- updateRoundedCorners(nonStartingAppW);
+ updateRoundedCornersIfNeeded(nonStartingAppW);
}
updateWallpaperForLetterbox(w);
@@ -533,7 +695,7 @@
// Note that we check the task rather than the parent as with ActivityEmbedding the parent might
// be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
// actually fullscreen.
- private boolean isDisplayFullScreenAndInPosture(DeviceStateController.FoldState state,
+ private boolean isDisplayFullScreenAndInPosture(DeviceStateController.DeviceState state,
boolean isTabletop) {
Task task = mActivityRecord.getTask();
return mActivityRecord.mDisplayContent != null
@@ -559,7 +721,7 @@
// Don't check resolved configuration because it may not be updated yet during
// configuration change.
boolean bookMode = isDisplayFullScreenAndInPosture(
- DeviceStateController.FoldState.HALF_FOLDED, false /* isTabletop */);
+ DeviceStateController.DeviceState.HALF_FOLDED, false /* isTabletop */);
return isHorizontalReachabilityEnabled(parentConfiguration)
// Using the last global dynamic position to avoid "jumps" when moving
// between apps or activities.
@@ -571,7 +733,7 @@
// Don't check resolved configuration because it may not be updated yet during
// configuration change.
boolean tabletopMode = isDisplayFullScreenAndInPosture(
- DeviceStateController.FoldState.HALF_FOLDED, true /* isTabletop */);
+ DeviceStateController.DeviceState.HALF_FOLDED, true /* isTabletop */);
return isVerticalReachabilityEnabled(parentConfiguration)
// Using the last global dynamic position to avoid "jumps" when moving
// between apps or activities.
@@ -724,6 +886,7 @@
* <li>Activity is portrait-only.
* <li>Fullscreen window in landscape device orientation.
* <li>Horizontal Reachability is enabled.
+ * <li>Activity fills parent vertically.
* </ul>
*/
private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
@@ -731,10 +894,14 @@
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
- && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT);
+ && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT)
+ // Check whether the activity fills the parent vertically.
+ && parentConfiguration.windowConfiguration.getBounds().height()
+ == mActivityRecord.getBounds().height();
}
- private boolean isHorizontalReachabilityEnabled() {
+ @VisibleForTesting
+ boolean isHorizontalReachabilityEnabled() {
return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
}
@@ -746,6 +913,7 @@
* <li>Activity is landscape-only.
* <li>Fullscreen window in portrait device orientation.
* <li>Vertical Reachability is enabled.
+ * <li>Activity fills parent horizontally.
* </ul>
*/
private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
@@ -753,10 +921,14 @@
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_PORTRAIT
- && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE);
+ && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE)
+ // Check whether the activity fills the parent horizontally.
+ && parentConfiguration.windowConfiguration.getBounds().width()
+ == mActivityRecord.getBounds().width();
}
- private boolean isVerticalReachabilityEnabled() {
+ @VisibleForTesting
+ boolean isVerticalReachabilityEnabled() {
return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
}
@@ -765,8 +937,8 @@
return isSurfaceReadyAndVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed()
// Check for FLAG_SHOW_WALLPAPER explicitly instead of using
// WindowContainer#showWallpaper because the later will return true when this
- // activity is using blurred wallpaper for letterbox backgroud.
- && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
+ // activity is using blurred wallpaper for letterbox background.
+ && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
}
@VisibleForTesting
@@ -818,106 +990,107 @@
return mLetterboxConfiguration.getLetterboxBackgroundColor();
}
- private void updateRoundedCorners(WindowState mainWindow) {
+ private void updateRoundedCornersIfNeeded(final WindowState mainWindow) {
final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
- if (windowSurface != null && windowSurface.isValid()) {
- final Transaction transaction = mActivityRecord.getSyncTransaction();
-
- if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
- // We don't want corner radius on the window.
- // In the case the ActivityRecord requires a letterboxed animation we never want
- // rounded corners on the window because rounded corners are applied at the
- // animation-bounds surface level and rounded corners on the window would interfere
- // with that leading to unexpected rounded corner positioning during the animation.
- transaction
- .setWindowCrop(windowSurface, null)
- .setCornerRadius(windowSurface, 0);
- return;
- }
-
- Rect cropBounds = null;
-
- if (hasVisibleTaskbar(mainWindow)) {
- cropBounds = new Rect(mActivityRecord.getBounds());
-
- // Rounded corners should be displayed above the taskbar.
- // It is important to call adjustBoundsForTaskbarUnchecked before offsetTo
- // because taskbar bounds are in screen coordinates
- adjustBoundsForTaskbarUnchecked(mainWindow, cropBounds);
-
- // Activity bounds are in screen coordinates while (0,0) for activity's surface
- // control is at the top left corner of an app window so offsetting bounds
- // accordingly.
- cropBounds.offsetTo(0, 0);
- }
-
- transaction
- .setWindowCrop(windowSurface, cropBounds)
- .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
+ if (windowSurface == null || !windowSurface.isValid()) {
+ return;
}
+
+ // cropBounds must be non-null for the cornerRadius to be ever applied.
+ mActivityRecord.getSyncTransaction()
+ .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
+ .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
}
- private boolean requiresRoundedCorners(WindowState mainWindow) {
- final InsetsSource taskbarInsetsSource = getTaskbarInsetsSource(mainWindow);
+ @VisibleForTesting
+ @Nullable
+ Rect getCropBoundsIfNeeded(final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
+ // We don't want corner radius on the window.
+ // In the case the ActivityRecord requires a letterboxed animation we never want
+ // rounded corners on the window because rounded corners are applied at the
+ // animation-bounds surface level and rounded corners on the window would interfere
+ // with that leading to unexpected rounded corner positioning during the animation.
+ return null;
+ }
+ final Rect cropBounds = new Rect(mActivityRecord.getBounds());
+
+ // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
+ // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
+ // are in screen coordinates
+ adjustBoundsIfNeeded(mainWindow, cropBounds);
+
+ // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
+ // control is in the top left corner of an app window so offsetting bounds
+ // accordingly.
+ cropBounds.offsetTo(0, 0);
+ return cropBounds;
+ }
+
+ private boolean requiresRoundedCorners(final WindowState mainWindow) {
return isLetterboxedNotForDisplayCutout(mainWindow)
- && mLetterboxConfiguration.isLetterboxActivityCornersRounded()
- && taskbarInsetsSource != null;
+ && mLetterboxConfiguration.isLetterboxActivityCornersRounded();
}
// Returns rounded corners radius the letterboxed activity should have based on override in
// R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
- // Device corners can be different on the right and left sides but we use the same radius
+ // Device corners can be different on the right and left sides, but we use the same radius
// for all corners for consistency and pick a minimal bottom one for consistency with a
// taskbar rounded corners.
- int getRoundedCornersRadius(WindowState mainWindow) {
- if (!requiresRoundedCorners(mainWindow)) {
+ int getRoundedCornersRadius(final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
return 0;
}
+ final int radius;
if (mLetterboxConfiguration.getLetterboxActivityCornersRadius() >= 0) {
- return mLetterboxConfiguration.getLetterboxActivityCornersRadius();
+ radius = mLetterboxConfiguration.getLetterboxActivityCornersRadius();
+ } else {
+ final InsetsState insetsState = mainWindow.getInsetsState();
+ radius = Math.min(
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
}
- final InsetsState insetsState = mainWindow.getInsetsState();
- return Math.min(
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+ final float scale = mainWindow.mInvGlobalScale;
+ return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
}
/**
- * Returns whether the taskbar is visible. Returns false if the window is in immersive mode,
- * since the user can swipe to show/hide the taskbar as an overlay.
+ * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
*/
- private boolean hasVisibleTaskbar(WindowState mainWindow) {
- final InsetsSource taskbarInsetsSource = getTaskbarInsetsSource(mainWindow);
-
- return taskbarInsetsSource != null
- && taskbarInsetsSource.isVisible();
+ @VisibleForTesting
+ @Nullable
+ InsetsSource getExpandedTaskbarOrNull(final WindowState mainWindow) {
+ final InsetsSource taskbar = mainWindow.getInsetsState().peekSource(
+ InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ if (taskbar != null && taskbar.isVisible()
+ && taskbar.getFrame().height() >= mExpandedTaskBarHeight) {
+ return taskbar;
+ }
+ return null;
}
- private InsetsSource getTaskbarInsetsSource(WindowState mainWindow) {
- final InsetsState insetsState = mainWindow.getInsetsState();
- return insetsState.peekSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
- }
-
- private void adjustBoundsForTaskbar(WindowState mainWindow, Rect bounds) {
+ private void adjustBoundsIfNeeded(final WindowState mainWindow, final Rect bounds) {
// Rounded corners should be displayed above the taskbar. When taskbar is hidden,
// an insets frame is equal to a navigation bar which shouldn't affect position of
// rounded corners since apps are expected to handle navigation bar inset.
// This condition checks whether the taskbar is visible.
// Do not crop the taskbar inset if the window is in immersive mode - the user can
// swipe to show/hide the taskbar as an overlay.
- if (hasVisibleTaskbar(mainWindow)) {
- adjustBoundsForTaskbarUnchecked(mainWindow, bounds);
+ // Adjust the bounds only in case there is an expanded taskbar,
+ // otherwise the rounded corners will be shown behind the navbar.
+ final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
+ if (expandedTaskbarOrNull != null) {
+ // Rounded corners should be displayed above the expanded taskbar.
+ bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
}
- }
- private void adjustBoundsForTaskbarUnchecked(WindowState mainWindow, Rect bounds) {
- // Rounded corners should be displayed above the taskbar.
- bounds.bottom =
- Math.min(bounds.bottom, getTaskbarInsetsSource(mainWindow).getFrame().top);
- scaleIfNeeded(bounds);
+ final float scale = mainWindow.mInvGlobalScale;
+ if (scale != 1f && scale > 0f) {
+ bounds.scale(scale);
+ }
}
private int getInsetsStateCornerRadius(
@@ -1087,7 +1260,7 @@
int letterboxPositionForHorizontalReachability = getLetterboxConfiguration()
.getLetterboxPositionForHorizontalReachability(
isDisplayFullScreenAndInPosture(
- DeviceStateController.FoldState.HALF_FOLDED,
+ DeviceStateController.DeviceState.HALF_FOLDED,
false /* isTabletop */));
positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
letterboxPositionForHorizontalReachability);
@@ -1095,7 +1268,7 @@
int letterboxPositionForVerticalReachability = getLetterboxConfiguration()
.getLetterboxPositionForVerticalReachability(
isDisplayFullScreenAndInPosture(
- DeviceStateController.FoldState.HALF_FOLDED,
+ DeviceStateController.DeviceState.HALF_FOLDED,
true /* isTabletop */));
positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
letterboxPositionForVerticalReachability);
@@ -1204,7 +1377,8 @@
// To avoid wrong behaviour, we're not forcing orientation for activities with not
// fixed orientation (e.g. permission dialogs).
return hasInheritedLetterboxBehavior()
- && mActivityRecord.mOrientation != SCREEN_ORIENTATION_UNSPECIFIED;
+ && mActivityRecord.getOverrideOrientation()
+ != SCREEN_ORIENTATION_UNSPECIFIED;
}
float getInheritedMinAspectRatio() {
@@ -1259,20 +1433,4 @@
mInheritedSizeCompatScale = 1f;
mInheritedCompatDisplayInsets = null;
}
-
- private void scaleIfNeeded(Rect bounds) {
- if (boundsNeedToScale()) {
- bounds.scale(1.0f / mActivityRecord.getCompatScale());
- }
- }
-
- private boolean boundsNeedToScale() {
- if (hasInheritedLetterboxBehavior()) {
- return mIsInheritedInSizeCompatMode
- && mInheritedSizeCompatScale < 1.0f;
- } else {
- return mActivityRecord.inSizeCompatMode()
- && mActivityRecord.getCompatScale() < 1.0f;
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index 30bdc34..2edb082 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -51,10 +51,10 @@
/**
* Called by the DeviceStateManager callback when the state changes.
*/
- void foldStateChanged(DeviceStateController.FoldState newFoldState) {
+ void foldStateChanged(DeviceStateController.DeviceState newDeviceState) {
// Ignore transitions to/from half-folded.
- if (newFoldState == DeviceStateController.FoldState.HALF_FOLDED) return;
- mIsFolded = newFoldState == DeviceStateController.FoldState.FOLDED;
+ if (newDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) return;
+ mIsFolded = newDeviceState == DeviceStateController.DeviceState.FOLDED;
}
/**
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index fd5c5eb..8ba54ff 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2086,8 +2086,10 @@
// When the Task is entering picture-in-picture, we should clear all override
// from the client organizer, so the PIP activity can get the correct config
// from the Task, and prevent conflict with the PipTaskOrganizer.
+ // TaskFragmentOrganizer may have requested relative bounds, so reset the
+ // relative bounds before update configuration.
+ tf.setRelativeEmbeddedBounds(new Rect());
tf.updateRequestedOverrideConfiguration(EMPTY);
- tf.updateRelativeEmbeddedBounds();
}
});
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 8a6ddf6..98ca9ae 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -143,7 +143,11 @@
.setLaunchTaskDisplayArea(options.getLaunchTaskDisplayArea())
.setLaunchDisplayId(options.getLaunchDisplayId())
.setCallerDisplayId(options.getCallerDisplayId())
- .setLaunchRootTask(options.getLaunchRootTask());
+ .setLaunchRootTask(options.getLaunchRootTask())
+ .setPendingIntentBackgroundActivityStartMode(
+ options.getPendingIntentBackgroundActivityStartMode())
+ .setIgnorePendingIntentCreatorForegroundState(
+ options.getIgnorePendingIntentCreatorForegroundState());
}
/**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1b59d8d..ee11f68 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -276,8 +276,6 @@
private static final int DEFAULT_MIN_TASK_SIZE_DP = 220;
- private float mShadowRadius = 0;
-
/**
* The modes to control how root task is moved to the front when calling {@link Task#reparent}.
*/
@@ -5020,70 +5018,76 @@
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
+ "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
- // The transition animation and starting window are not needed if {@code allowMoveToFront}
- // is false, because the activity won't be visible.
- if ((!isActivityTypeHomeOrRecents() || hasActivity()) && allowMoveToFront) {
- final DisplayContent dc = mDisplayContent;
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare open transition: starting " + r);
- if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- dc.prepareAppTransition(TRANSIT_NONE);
- mTaskSupervisor.mNoAnimActivities.add(r);
- mTransitionController.setNoAnimation(r);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN);
- mTaskSupervisor.mNoAnimActivities.remove(r);
- }
- if (newTask && !r.mLaunchTaskBehind) {
- // If a new task is being launched, then mark the existing top activity as
- // supporting picture-in-picture while pausing only if the starting activity
- // would not be considered an overlay on top of the current activity
- // (eg. not fullscreen, or the assistant)
- enableEnterPipOnTaskSwitch(pipCandidate,
- null /* toFrontTask */, r, options);
- }
- boolean doShow = true;
- if (newTask) {
- // Even though this activity is starting fresh, we still need
- // to reset it to make sure we apply affinities to move any
- // existing activities from other tasks in to it.
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- resetTaskIfNeeded(r, r);
- doShow = topRunningNonDelayedActivityLocked(null) == r;
- }
- } else if (options != null && options.getAnimationType()
- == ActivityOptions.ANIM_SCENE_TRANSITION) {
- doShow = false;
- }
- if (options != null && options.getDisableStartingWindow()) {
- doShow = false;
- }
- if (r.mLaunchTaskBehind) {
- // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
- // tell WindowManager that r is visible even though it is at the back of the root
- // task.
- r.setVisibility(true);
- ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- // Go ahead to execute app transition for this activity since the app transition
- // will not be triggered through the resume channel.
- mDisplayContent.executeAppTransition();
- } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
- // Figure out if we are transitioning from another activity that is
- // "has the same starting icon" as the next one. This allows the
- // window manager to keep the previous window it had previously
- // created, if it still had one.
- Task baseTask = r.getTask();
- final ActivityRecord prev = baseTask.getActivity(
- a -> a.mStartingData != null && a.showToCurrentUser());
- mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,
- isTaskSwitch, sourceRecord);
- }
- } else {
+ if (isActivityTypeHomeOrRecents() && getActivityBelow(r) == null) {
// If this is the first activity, don't do any fancy animations,
// because there is nothing for it to animate on top of.
ActivityOptions.abort(options);
+ return;
+ }
+
+ if (!allowMoveToFront) {
+ // The transition animation and starting window are not needed if
+ // {@code allowMoveToFront} is false, because the activity won't be visible.
+ ActivityOptions.abort(options);
+ return;
+ }
+
+ final DisplayContent dc = mDisplayContent;
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+ "Prepare open transition: starting " + r);
+ if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ dc.prepareAppTransition(TRANSIT_NONE);
+ mTaskSupervisor.mNoAnimActivities.add(r);
+ mTransitionController.setNoAnimation(r);
+ } else {
+ dc.prepareAppTransition(TRANSIT_OPEN);
+ mTaskSupervisor.mNoAnimActivities.remove(r);
+ }
+ if (newTask && !r.mLaunchTaskBehind) {
+ // If a new task is being launched, then mark the existing top activity as
+ // supporting picture-in-picture while pausing only if the starting activity
+ // would not be considered an overlay on top of the current activity
+ // (eg. not fullscreen, or the assistant)
+ enableEnterPipOnTaskSwitch(pipCandidate,
+ null /* toFrontTask */, r, options);
+ }
+ boolean doShow = true;
+ if (newTask) {
+ // Even though this activity is starting fresh, we still need
+ // to reset it to make sure we apply affinities to move any
+ // existing activities from other tasks in to it.
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ resetTaskIfNeeded(r, r);
+ doShow = topRunningNonDelayedActivityLocked(null) == r;
+ }
+ } else if (options != null && options.getAnimationType()
+ == ActivityOptions.ANIM_SCENE_TRANSITION) {
+ doShow = false;
+ }
+ if (options != null && options.getDisableStartingWindow()) {
+ doShow = false;
+ }
+ if (r.mLaunchTaskBehind) {
+ // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
+ // tell WindowManager that r is visible even though it is at the back of the root
+ // task.
+ r.setVisibility(true);
+ ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ // Go ahead to execute app transition for this activity since the app transition
+ // will not be triggered through the resume channel.
+ mDisplayContent.executeAppTransition();
+ } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
+ // Figure out if we are transitioning from another activity that is
+ // "has the same starting icon" as the next one. This allows the
+ // window manager to keep the previous window it had previously
+ // created, if it still had one.
+ Task baseTask = r.getTask();
+ final ActivityRecord prev = baseTask.getActivity(
+ a -> a.mStartingData != null && a.showToCurrentUser());
+ mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,
+ isTaskSwitch, sourceRecord);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index db17ae1..eb06b91 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -312,9 +312,8 @@
private TaskFragmentAnimationParams mAnimationParams = TaskFragmentAnimationParams.DEFAULT;
/**
- * The bounds of the embedded TaskFragment relative to the parent Task.
+ * The organizer requested bounds of the embedded TaskFragment relative to the parent Task.
* {@code null} if it is not {@link #mIsEmbedded}
- * TODO(b/261785978) cleanup with legacy app transition
*/
@Nullable
private final Rect mRelativeEmbeddedBounds;
@@ -335,6 +334,7 @@
final Point mLastSurfaceSize = new Point();
private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpAbsBounds = new Rect();
private final Rect mTmpFullBounds = new Rect();
/** For calculating screenWidthDp and screenWidthDp, i.e. the area without the system bars. */
private final Rect mTmpStableBounds = new Rect();
@@ -1966,16 +1966,21 @@
void resolveOverrideConfiguration(Configuration newParentConfig) {
mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
super.resolveOverrideConfiguration(newParentConfig);
+ final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ if (mRelativeEmbeddedBounds != null && !mRelativeEmbeddedBounds.isEmpty()) {
+ // For embedded TaskFragment, make sure the bounds is set based on the relative bounds.
+ resolvedConfig.windowConfiguration.setBounds(translateRelativeBoundsToAbsoluteBounds(
+ mRelativeEmbeddedBounds, newParentConfig.windowConfiguration.getBounds()));
+ }
+ int windowingMode = resolvedConfig.windowConfiguration.getWindowingMode();
final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
// Resolve override windowing mode to fullscreen for home task (even on freeform
// display), or split-screen if in split-screen mode.
if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = WINDOWING_MODE_FULLSCREEN;
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ resolvedConfig.windowConfiguration.setWindowingMode(windowingMode);
}
// Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
@@ -1985,8 +1990,7 @@
windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
&& candidateWindowingMode != WINDOWING_MODE_PINNED) {
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
- WINDOWING_MODE_FULLSCREEN);
+ resolvedConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
}
@@ -1995,7 +1999,7 @@
thisTask.resolveLeafTaskOnlyOverrideConfigs(newParentConfig,
mTmpBounds /* previousBounds */);
}
- computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ computeConfigResourceOverrides(resolvedConfig, newParentConfig);
}
boolean supportsMultiWindow() {
@@ -2422,17 +2426,55 @@
}
/**
- * Updates the record of the relative bounds of this embedded TaskFragment. This should only be
- * called when the embedded TaskFragment's override bounds are changed.
- * Returns {@code true} if the bounds is changed.
+ * Translates the given relative bounds to screen space based on the given parent bounds.
+ * When the relative bounds is outside of the parent bounds, it will be adjusted to fit the Task
+ * bounds.
*/
- void updateRelativeEmbeddedBounds() {
- // We only record the override bounds, which means it will not be changed when it is filling
- // Task, and resize with the parent.
- getRequestedOverrideBounds(mTmpBounds);
- getRelativePosition(mTmpPoint);
- mTmpBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
- mRelativeEmbeddedBounds.set(mTmpBounds);
+ Rect translateRelativeBoundsToAbsoluteBounds(@NonNull Rect relativeBounds,
+ @NonNull Rect parentBounds) {
+ if (relativeBounds.isEmpty()) {
+ mTmpAbsBounds.setEmpty();
+ return mTmpAbsBounds;
+ }
+ // Translate the relative bounds to absolute bounds.
+ mTmpAbsBounds.set(relativeBounds);
+ mTmpAbsBounds.offset(parentBounds.left, parentBounds.top);
+
+ if (!isAllowedToBeEmbeddedInTrustedMode() && !parentBounds.contains(mTmpAbsBounds)) {
+ // For untrusted embedding, we want to make sure the embedded bounds will never go
+ // outside of the Task bounds.
+ // We expect the organizer to update the bounds after receiving the Task bounds changed,
+ // so skip trusted embedding to avoid unnecessary configuration change before organizer
+ // requests a new bounds.
+ // When the requested TaskFragment bounds is outside of Task bounds, try use the
+ // intersection.
+ // This can happen when the Task resized before the TaskFragmentOrganizer request.
+ if (!mTmpAbsBounds.intersect(parentBounds)) {
+ // Use empty bounds to fill Task if there is no intersection.
+ mTmpAbsBounds.setEmpty();
+ }
+ }
+ return mTmpAbsBounds;
+ }
+
+ void recomputeConfiguration() {
+ onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ }
+
+ /**
+ * Sets the relative bounds in parent coordinate for this embedded TaskFragment.
+ * This will not override the requested bounds, and the actual bounds will be calculated in
+ * {@link #resolveOverrideConfiguration}, so that it makes sure to record and use the relative
+ * bounds that is set by the organizer until the organizer requests a new relative bounds.
+ */
+ void setRelativeEmbeddedBounds(@NonNull Rect relativeEmbeddedBounds) {
+ if (mRelativeEmbeddedBounds == null) {
+ throw new IllegalStateException("The TaskFragment is not embedded");
+ }
+ if (mRelativeEmbeddedBounds.equals(relativeEmbeddedBounds)) {
+ return;
+ }
+ mRelativeEmbeddedBounds.set(relativeEmbeddedBounds);
}
/**
@@ -2458,6 +2500,13 @@
}
}
+ @Override
+ boolean canStartChangeTransition() {
+ final Task task = getTask();
+ // Skip change transition when the Task is drag resizing.
+ return task != null && !task.isDragResizing() && super.canStartChangeTransition();
+ }
+
/** Records the starting bounds of the closing organized TaskFragment. */
void setClosingChangingStartBoundsIfNeeded() {
if (isOrganizedTaskFragment() && mDisplayContent != null
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7d3367f..d2f30ce 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -33,6 +33,8 @@
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -1302,8 +1304,13 @@
private void handleNonAppWindowsInTransition(@NonNull DisplayContent dc,
@TransitionType int transit, @TransitionFlags int flags) {
if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
- mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(
- false /* notify */);
+ // If the occlusion changed but the transition isn't an occlude/unocclude transition,
+ // then we have to notify KeyguardService directly. This can happen if there is
+ // another ongoing transition when the app changes occlusion OR if the app dies or
+ // is killed. Both of these are common during tests.
+ final boolean notify = !(transit == TRANSIT_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_KEYGUARD_UNOCCLUDE);
+ mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(notify);
}
}
@@ -1650,6 +1657,12 @@
// DisplayContent is the "root", so we reinterpret it's wc as the window layer
// making the parent surface the displaycontent's surface.
return wc.getSurfaceControl();
+ } else if (wc.getParent().asDisplayContent() != null) {
+ // DisplayContent is kinda split into 2 pieces, the "real root" and the
+ // "windowing layer". So if the parent of the window is DC, then it really belongs on
+ // the windowing layer (unless it's an overlay display area, but those can't be in
+ // transitions anyways).
+ return wc.getParent().asDisplayContent().getWindowingLayer();
}
return wc.getParent().getSurfaceControl();
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 5e116ba..80965a7 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -730,7 +730,7 @@
void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
for (int i = 0; i < mLegacyListeners.size(); ++i) {
- // TODO(shell-transitions): handle (un)occlude transition.
+ mLegacyListeners.get(i).onAppTransitionStartingLocked(info);
mLegacyListeners.get(i).onAppTransitionStartingLocked(
SystemClock.uptimeMillis() + statusBarTransitionDelay,
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 63bb5c3..b06bdb1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -73,6 +73,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Point;
@@ -178,8 +179,9 @@
protected final WindowList<E> mChildren = new WindowList<E>();
// The specified orientation for this window container.
- @ActivityInfo.ScreenOrientation
- protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ // Shouldn't be accessed directly since subclasses can override getOverrideOrientation.
+ @ScreenOrientation
+ private int mOverrideOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
/**
* The window container which decides its orientation since the last time
@@ -1456,19 +1458,20 @@
/**
* Gets the configuration orientation by the requested screen orientation
- * ({@link ActivityInfo.ScreenOrientation}) of this activity.
+ * ({@link ScreenOrientation}) of this activity.
*
* @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
* {@link Configuration#ORIENTATION_PORTRAIT},
* {@link Configuration#ORIENTATION_UNDEFINED}).
*/
+ @ScreenOrientation
int getRequestedConfigurationOrientation() {
return getRequestedConfigurationOrientation(false /* forDisplay */);
}
/**
* Gets the configuration orientation by the requested screen orientation
- * ({@link ActivityInfo.ScreenOrientation}) of this activity.
+ * ({@link ScreenOrientation}) of this activity.
*
* @param forDisplay whether it is the requested config orientation for display.
* If {@code true}, we may reverse the requested orientation if the root is
@@ -1479,8 +1482,9 @@
* {@link Configuration#ORIENTATION_PORTRAIT},
* {@link Configuration#ORIENTATION_UNDEFINED}).
*/
+ @ScreenOrientation
int getRequestedConfigurationOrientation(boolean forDisplay) {
- int requestedOrientation = mOrientation;
+ int requestedOrientation = getOverrideOrientation();
final RootDisplayArea root = getRootDisplayArea();
if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
// Reverse the requested orientation if the orientation of its root is different from
@@ -1490,7 +1494,7 @@
// (portrait).
// When an app below the DAG is requesting landscape, it should actually request the
// display to be portrait, so that the DAG and the app will be in landscape.
- requestedOrientation = reverseOrientation(mOrientation);
+ requestedOrientation = reverseOrientation(getOverrideOrientation());
}
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
@@ -1515,7 +1519,7 @@
*
* @param orientation the specified orientation.
*/
- void setOrientation(int orientation) {
+ void setOrientation(@ScreenOrientation int orientation) {
setOrientation(orientation, null /* requestingContainer */);
}
@@ -1523,17 +1527,17 @@
* Sets the specified orientation of this container. It percolates this change upward along the
* hierarchy to let each level of the hierarchy a chance to respond to it.
*
- * @param orientation the specified orientation. Needs to be one of {@link
- * android.content.pm.ActivityInfo.ScreenOrientation}.
+ * @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}.
* @param requestingContainer the container which orientation request has changed. Mostly used
* to ensure it gets correct configuration.
*/
- void setOrientation(int orientation, @Nullable WindowContainer requestingContainer) {
- if (mOrientation == orientation) {
+ void setOrientation(@ScreenOrientation int orientation,
+ @Nullable WindowContainer requestingContainer) {
+ if (getOverrideOrientation() == orientation) {
return;
}
- mOrientation = orientation;
+ setOverrideOrientation(orientation);
final WindowContainer parent = getParent();
if (parent != null) {
if (getConfiguration().orientation != getRequestedConfigurationOrientation()
@@ -1552,9 +1556,9 @@
}
}
- @ActivityInfo.ScreenOrientation
+ @ScreenOrientation
int getOrientation() {
- return getOrientation(mOrientation);
+ return getOrientation(getOverrideOrientation());
}
/**
@@ -1568,7 +1572,8 @@
* better match.
* @return The orientation as specified by this branch or the window hierarchy.
*/
- int getOrientation(int candidate) {
+ @ScreenOrientation
+ int getOrientation(@ScreenOrientation int candidate) {
mLastOrientationSource = null;
if (!providesOrientation()) {
return SCREEN_ORIENTATION_UNSET;
@@ -1578,16 +1583,16 @@
// specified; otherwise we prefer to use the orientation of its topmost child that has one
// specified and fall back on this container's unset or unspecified value as a candidate
// if none of the children have a better candidate for the orientation.
- if (mOrientation != SCREEN_ORIENTATION_UNSET
- && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+ if (getOverrideOrientation() != SCREEN_ORIENTATION_UNSET
+ && getOverrideOrientation() != SCREEN_ORIENTATION_UNSPECIFIED) {
mLastOrientationSource = this;
- return mOrientation;
+ return getOverrideOrientation();
}
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
- // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs.
+ // TODO: Maybe mOverrideOrientation should default to SCREEN_ORIENTATION_UNSET vs.
// SCREEN_ORIENTATION_UNSPECIFIED?
final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
@@ -1619,6 +1624,20 @@
}
/**
+ * Returns orientation specified on this level of hierarchy without taking children into
+ * account, like {@link #getOrientation} does, allowing subclasses to override. See {@link
+ * ActivityRecord#getOverrideOrientation} for an example.
+ */
+ @ScreenOrientation
+ protected int getOverrideOrientation() {
+ return mOverrideOrientation;
+ }
+
+ protected void setOverrideOrientation(@ScreenOrientation int orientation) {
+ mOverrideOrientation = orientation;
+ }
+
+ /**
* @return The deepest source which decides the orientation of this window container since the
* last time {@link #getOrientation(int) was called.
*/
@@ -2674,7 +2693,7 @@
final long token = proto.start(fieldId);
super.dumpDebug(proto, CONFIGURATION_CONTAINER, logLevel);
- proto.write(ORIENTATION, mOrientation);
+ proto.write(ORIENTATION, mOverrideOrientation);
proto.write(VISIBLE, isVisible);
writeIdentifierToProto(proto, IDENTIFIER);
if (mSurfaceAnimator.isAnimating()) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1282acb..ad6bd3c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -44,6 +44,7 @@
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
import android.view.inputmethod.ImeTracker;
+import android.window.TransitionInfo;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
@@ -212,7 +213,7 @@
* Abstract class to be notified about {@link com.android.server.wm.AppTransition} events. Held
* as an abstract class so a listener only needs to implement the methods of its interest.
*/
- public static abstract class AppTransitionListener {
+ public abstract static class AppTransitionListener {
/**
* Called when an app transition is being setup and about to be executed.
@@ -251,6 +252,20 @@
}
/**
+ * Called when an app transition gets started when WM shell is enabled.
+ *
+ * @param info Information about what is changing during a transition.
+ *
+ * @return Return any bit set of {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT},
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG},
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
+ * or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
+ */
+ public int onAppTransitionStartingLocked(TransitionInfo info) {
+ return 0;
+ }
+
+ /**
* Called when an app transition is finished running.
*
* @param token the token for app whose transition has finished
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dc32769..f6cb068 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -26,6 +26,7 @@
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
@@ -168,6 +169,7 @@
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -3130,7 +3132,7 @@
@Override
public void notifyKeyguardTrustedChanged() {
synchronized (mGlobalLock) {
- if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
+ if (mAtmService.mKeyguardController.isLocksScreenShowing(DEFAULT_DISPLAY)) {
mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
}
}
@@ -9447,4 +9449,55 @@
public void markSurfaceSyncGroupReady(IBinder syncGroupToken) {
mSurfaceSyncGroupController.markSyncGroupReady(syncGroupToken);
}
+
+ private ArraySet<ActivityRecord> getVisibleActivityRecords(int displayId) {
+ ArraySet<ActivityRecord> result = new ArraySet<>();
+ synchronized (mGlobalLock) {
+ ArraySet<ComponentName> addedActivities = new ArraySet<>();
+ DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.forAllWindows(
+ (w) -> {
+ if (w.isVisible()
+ && w.isDisplayed()
+ && w.mActivityRecord != null
+ && !addedActivities.contains(
+ w.mActivityRecord.mActivityComponent)
+ && w.mActivityRecord.isVisible()
+ && w.isVisibleNow()) {
+ addedActivities.add(w.mActivityRecord.mActivityComponent);
+ result.add(w.mActivityRecord);
+ }
+ },
+ true /* traverseTopToBottom */);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Must be called when a screenshot is taken via hardware chord.
+ *
+ * Notifies all registered visible activities that have registered for screencapture callback,
+ * Returns a list of visible apps component names.
+ */
+ @Override
+ public List<ComponentName> notifyScreenshotListeners(int displayId) {
+ // make sure caller is SysUI.
+ if (!checkCallingPermission(STATUS_BAR_SERVICE,
+ "notifyScreenshotListeners()")) {
+ throw new SecurityException("Requires STATUS_BAR_SERVICE permission");
+ }
+ synchronized (mGlobalLock) {
+ ArraySet<ComponentName> notifiedApps = new ArraySet<>();
+ ArraySet<ActivityRecord> visibleApps = getVisibleActivityRecords(displayId);
+ for (ActivityRecord ar : visibleApps) {
+ if (ar.isRegisteredForScreenCaptureCallback()) {
+ ar.reportScreenCaptured();
+ notifiedApps.add(ar.mActivityComponent);
+ }
+ }
+ return List.copyOf(notifiedApps);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5c68b12..29d3462 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -31,6 +31,7 @@
import static android.window.TaskFragmentOperation.OP_TYPE_SET_COMPANION_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
+import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
@@ -627,8 +628,8 @@
}
}
- private int applyChanges(WindowContainer<?> container,
- WindowContainerTransaction.Change change, @Nullable IBinder errorCallbackToken) {
+ private int applyChanges(@NonNull WindowContainer<?> container,
+ @NonNull WindowContainerTransaction.Change change) {
// The "client"-facing API should prevent bad changes; however, just in case, sanitize
// masks here.
final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
@@ -636,9 +637,6 @@
int effects = TRANSACT_EFFECTS_NONE;
final int windowingMode = change.getWindowingMode();
if (configMask != 0) {
-
- adjustBoundsForMinDimensionsIfNeeded(container, change, errorCallbackToken);
-
if (windowingMode > -1 && windowingMode != container.getWindowingMode()) {
// Special handling for when we are setting a windowingMode in the same transaction.
// Setting the windowingMode is going to call onConfigurationChanged so we don't
@@ -691,28 +689,8 @@
return effects;
}
- private void adjustBoundsForMinDimensionsIfNeeded(WindowContainer<?> container,
- WindowContainerTransaction.Change change, @Nullable IBinder errorCallbackToken) {
- final TaskFragment taskFragment = container.asTaskFragment();
- if (taskFragment == null || !taskFragment.isEmbedded()) {
- return;
- }
- if ((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) == 0) {
- return;
- }
- final WindowConfiguration winConfig = change.getConfiguration().windowConfiguration;
- final Rect bounds = winConfig.getBounds();
- final Point minDimensions = taskFragment.calculateMinDimension();
- if (bounds.width() < minDimensions.x || bounds.height() < minDimensions.y) {
- sendMinimumDimensionViolation(taskFragment, minDimensions, errorCallbackToken,
- "setBounds:" + bounds);
- // Sets the bounds to match parent bounds.
- winConfig.setBounds(new Rect());
- }
- }
-
private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
- int effects = applyChanges(tr, c, null /* errorCallbackToken */);
+ int effects = applyChanges(tr, c);
final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
@@ -773,7 +751,7 @@
private int applyDisplayAreaChanges(DisplayArea displayArea,
WindowContainerTransaction.Change c) {
final int[] effects = new int[1];
- effects[0] = applyChanges(displayArea, c, null /* errorCallbackToken */);
+ effects[0] = applyChanges(displayArea, c);
if ((c.getChangeMask()
& WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
@@ -807,8 +785,30 @@
mTmpBounds0.set(taskFragment.getBounds());
mTmpBounds1.set(taskFragment.getRelativeEmbeddedBounds());
taskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
- final int effects = applyChanges(taskFragment, c, errorCallbackToken);
- taskFragment.updateRelativeEmbeddedBounds();
+ final Rect relBounds = c.getRelativeBounds();
+ if (relBounds != null) {
+ // Make sure the TaskFragment bounds satisfied the min dimensions requirement.
+ adjustTaskFragmentBoundsForMinDimensionsIfNeeded(taskFragment, relBounds,
+ errorCallbackToken);
+
+ // For embedded TaskFragment, the organizer set the bounds in parent coordinate to
+ // prevent flicker in case there is a racing condition between the parent bounds changed
+ // and the organizer request.
+ final Rect parentBounds = taskFragment.getParent().getBounds();
+ // Convert relative bounds to screen space.
+ final Rect absBounds = taskFragment.translateRelativeBoundsToAbsoluteBounds(relBounds,
+ parentBounds);
+ c.getConfiguration().windowConfiguration.setBounds(absBounds);
+ taskFragment.setRelativeEmbeddedBounds(relBounds);
+ } else if ((c.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0) {
+ // TODO(b/265271880): remove after we drop support to setBounds for TaskFragment in next
+ // release.
+ adjustTaskFragmentBoundsForMinDimensionsIfNeeded(taskFragment, c.getConfiguration()
+ .windowConfiguration.getBounds(), errorCallbackToken);
+ // Reset the relative embedded bounds if WCT#setBounds is used instead for CTS compat.
+ taskFragment.setRelativeEmbeddedBounds(new Rect());
+ }
+ final int effects = applyChanges(taskFragment, c);
if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
taskFragment.initializeChangeTransition(mTmpBounds0);
}
@@ -816,6 +816,29 @@
return effects;
}
+ /**
+ * Adjusts the override absolute bounds on {@link TaskFragment} to make sure it satisfies the
+ * activity min dimensions.
+ */
+ private void adjustTaskFragmentBoundsForMinDimensionsIfNeeded(
+ @NonNull TaskFragment taskFragment, @NonNull Rect inOutBounds,
+ @Nullable IBinder errorCallbackToken) {
+ if (inOutBounds.isEmpty()) {
+ return;
+ }
+ final Point minDimensions = taskFragment.calculateMinDimension();
+ if (inOutBounds.width() < minDimensions.x || inOutBounds.height() < minDimensions.y) {
+ // Reset to match parent bounds.
+ inOutBounds.setEmpty();
+ // Notify organizer about the request failure.
+ final Throwable exception = new SecurityException("The task fragment's bounds:"
+ + taskFragment.getBounds() + " does not satisfy minimum dimensions:"
+ + minDimensions);
+ sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
+ errorCallbackToken, taskFragment, OP_TYPE_UNKNOWN, exception);
+ }
+ }
+
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
@@ -1312,19 +1335,6 @@
return true;
}
- /** A helper method to send minimum dimension violation error to the client. */
- private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
- IBinder errorCallbackToken, String reason) {
- if (taskFragment == null || taskFragment.getTaskFragmentOrganizer() == null) {
- return;
- }
- final Throwable exception = new SecurityException("The task fragment's bounds:"
- + taskFragment.getBounds() + " does not satisfy minimum dimensions:"
- + minDimensions + " " + reason);
- sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
- errorCallbackToken, taskFragment, OP_TYPE_UNKNOWN, exception);
- }
-
/**
* Post and wait for the result of the activity start to prevent potential deadlock against
* {@link WindowManagerGlobalLock}.
@@ -1579,10 +1589,10 @@
return applyDisplayAreaChanges(wc.asDisplayArea(), c);
} else if (wc.asTask() != null) {
return applyTaskChanges(wc.asTask(), c);
- } else if (wc.asTaskFragment() != null) {
+ } else if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbedded()) {
return applyTaskFragmentChanges(wc.asTaskFragment(), c, errorCallbackToken);
} else {
- return applyChanges(wc, c, errorCallbackToken);
+ return applyChanges(wc, c);
}
}
@@ -1799,8 +1809,13 @@
return;
}
final int changeMask = change.getChangeMask();
- if (changeMask != 0) {
- // None of the change should be requested from a TaskFragment organizer.
+ if (changeMask != 0 && changeMask != CHANGE_RELATIVE_BOUNDS) {
+ // None of the change should be requested from a TaskFragment organizer except
+ // setRelativeBounds.
+ // For setRelativeBounds, we don't need to check whether it is outside of the Task
+ // bounds, because it is possible that the Task is also resizing, for which we don't
+ // want to throw an exception. The bounds will be adjusted in
+ // TaskFragment#translateRelativeBoundsToAbsoluteBounds.
String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " trying to apply changes of " + changeMask + " to TaskFragment"
@@ -1818,6 +1833,7 @@
Slog.e(TAG, "Attempt to apply config change on task fragment that has no parent");
return;
}
+ // TODO(b/265271880): we can remove those and only support WCT#setRelativeBounds.
final Configuration requestedConfig = change.getConfiguration();
final Configuration parentConfig = wcParent.getConfiguration();
if (parentConfig.screenWidthDp < requestedConfig.screenWidthDp
@@ -1932,9 +1948,17 @@
}
ownerTask.addChild(taskFragment, position);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
- taskFragment.setBounds(creationParams.getInitialBounds());
- // Record the initial relative embedded bounds.
- taskFragment.updateRelativeEmbeddedBounds();
+ if (creationParams.areInitialRelativeBoundsSet()) {
+ // Set relative bounds instead of using setBounds. This will avoid unnecessary update in
+ // case the parent has resized since the last time parent info is sent to the organizer.
+ taskFragment.setRelativeEmbeddedBounds(creationParams.getInitialRelativeBounds());
+ // Recompute configuration as the bounds will be calculated based on relative bounds in
+ // TaskFragment#resolveOverrideConfiguration.
+ taskFragment.recomputeConfiguration();
+ } else {
+ // TODO(b/232476698): remove after remove creationParams.getInitialBounds().
+ taskFragment.setBounds(creationParams.getInitialBounds());
+ }
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
if (transition != null) transition.collectExistenceChange(taskFragment);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2980c76..7dfc132 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1015,7 +1015,7 @@
/**
* Returns display UI context list which there is any app window shows or starting activities
- * int this process.
+ * in this process.
*/
public void getDisplayContextsWithErrorDialogs(List<Context> displayContexts) {
if (displayContexts == null) {
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index d3d69ae..4af685e 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -146,6 +146,7 @@
// vmas vector, instead it iterates on it until the end.
class VmaBatchCreator {
const std::vector<Vma>* sourceVmas;
+ const int totalVmasInSource;
// This is the destination array where batched VMAs will be stored
// it gets encapsulated into a VmaBatch which is the object
// meant to be used by client code.
@@ -156,8 +157,13 @@
uint64_t currentOffset_;
public:
- VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec)
- : sourceVmas(vmasToBatch), destVmas(destVmasVec), currentIndex_(0), currentOffset_(0) {}
+ VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec,
+ int vmasInSource)
+ : sourceVmas(vmasToBatch),
+ totalVmasInSource(vmasInSource),
+ destVmas(destVmasVec),
+ currentIndex_(0),
+ currentOffset_(0) {}
int currentIndex() { return currentIndex_; }
uint64_t currentOffset() { return currentOffset_; }
@@ -177,7 +183,7 @@
// Add VMAs to the batch up until we consumed all the VMAs or
// reached any imposed limit of VMAs per batch.
- while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < vmas.size()) {
+ while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < totalVmasInSource) {
uint64_t vmaStart = vmas[currentIndex_].start + currentOffset_;
uint64_t vmaSize = vmas[currentIndex_].end - vmaStart;
uint64_t bytesAvailableInBatch = MAX_BYTES_PER_BATCH - totalBytesInBatch;
@@ -272,8 +278,9 @@
//
// If any VMA fails compaction due to -EINVAL it will be skipped and continue.
// However, if it fails for any other reason, it will bail out and forward the error
-static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
- if (vmas.empty()) {
+static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType,
+ int totalVmas) {
+ if (totalVmas == 0) {
return 0;
}
@@ -286,7 +293,7 @@
struct iovec destVmas[MAX_VMAS_PER_BATCH];
VmaBatch batch;
- VmaBatchCreator batcher(&vmas, destVmas);
+ VmaBatchCreator batcher(&vmas, destVmas, totalVmas);
int64_t totalBytesProcessed = 0;
while (batcher.createNextBatch(batch)) {
@@ -346,34 +353,53 @@
// Returns the total number of bytes compacted on success. On error
// returns process_madvise errno code or if compaction was cancelled
// it returns ERROR_COMPACTION_CANCELLED.
+//
+// Not thread safe. We reuse vectors so we assume this is called only
+// on one thread at most.
static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
cancelRunningCompaction.store(false);
-
+ static std::string mapsBuffer;
ATRACE_BEGIN("CollectVmas");
ProcMemInfo meminfo(pid);
- std::vector<Vma> pageoutVmas, coldVmas;
- auto vmaCollectorCb = [&coldVmas,&pageoutVmas,&vmaToAdviseFunc](const Vma& vma) {
+ static std::vector<Vma> pageoutVmas(2000), coldVmas(2000);
+ int coldVmaIndex = 0;
+ int pageoutVmaIndex = 0;
+ auto vmaCollectorCb = [&vmaToAdviseFunc, &pageoutVmaIndex, &coldVmaIndex](const Vma& vma) {
int advice = vmaToAdviseFunc(vma);
switch (advice) {
case MADV_COLD:
- coldVmas.push_back(vma);
+ if (coldVmaIndex < coldVmas.size()) {
+ coldVmas[coldVmaIndex] = vma;
+ } else {
+ coldVmas.push_back(vma);
+ }
+ ++coldVmaIndex;
break;
case MADV_PAGEOUT:
- pageoutVmas.push_back(vma);
+ if (pageoutVmaIndex < pageoutVmas.size()) {
+ pageoutVmas[pageoutVmaIndex] = vma;
+ } else {
+ pageoutVmas.push_back(vma);
+ }
+ ++pageoutVmaIndex;
break;
}
};
- meminfo.ForEachVmaFromMaps(vmaCollectorCb);
+ meminfo.ForEachVmaFromMaps(vmaCollectorCb, mapsBuffer);
ATRACE_END();
+#ifdef DEBUG_COMPACTION
+ ALOGE("Total VMAs sent for compaction anon=%d file=%d", pageoutVmaIndex,
+ coldVmaIndex);
+#endif
- int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
+ int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT, pageoutVmaIndex);
if (pageoutBytes < 0) {
// Error, just forward it.
cancelRunningCompaction.store(false);
return pageoutBytes;
}
- int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD);
+ int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD, coldVmaIndex);
if (coldBytes < 0) {
// Error, just forward it.
cancelRunningCompaction.store(false);
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 1859333..ec42324 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/gui/IHdrConversionConstants.h>
#include <android_util_Binder.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
@@ -55,6 +56,58 @@
}
}
+static void nativeSetHdrConversionMode(JNIEnv* env, jclass clazz, jint hdrConversionMode,
+ jint preferredHdrOutputType, jintArray autoHdrOutputTypes,
+ jint autoHdrOutputTypesLength) {
+ gui::HdrConversionStrategy hdrConversionStrategy;
+ switch (hdrConversionMode) {
+ case gui::IHdrConversionConstants::HdrConversionModePassthrough: {
+ hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::passthrough>(true);
+ break;
+ }
+ case gui::IHdrConversionConstants::HdrConversionModeAuto: {
+ jint* autoHdrOutputTypesArray = env->GetIntArrayElements(autoHdrOutputTypes, 0);
+ std::vector<int> autoHdrOutputTypesVector(autoHdrOutputTypesLength);
+ for (int i = 0; i < autoHdrOutputTypesLength; i++) {
+ autoHdrOutputTypesVector[i] = autoHdrOutputTypesArray[i];
+ }
+ hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::autoAllowedHdrTypes>(
+ autoHdrOutputTypesVector);
+ break;
+ }
+ case gui::IHdrConversionConstants::HdrConversionModeForce: {
+ hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::forceHdrConversion>(
+ preferredHdrOutputType);
+ break;
+ }
+ }
+
+ SurfaceComposerClient::setHdrConversionStrategy(hdrConversionStrategy);
+}
+
+static jintArray nativeGetSupportedHdrOutputTypes(JNIEnv* env, jclass clazz) {
+ std::vector<gui::HdrConversionCapability> hdrConversionCapabilities;
+ SurfaceComposerClient::getHdrConversionCapabilities(&hdrConversionCapabilities);
+
+ // Extract unique HDR output types.
+ std::set<int> hdrOutputTypes;
+ for (const auto& hdrConversionCapability : hdrConversionCapabilities) {
+ hdrOutputTypes.insert(hdrConversionCapability.outputType);
+ }
+ jintArray array = env->NewIntArray(hdrOutputTypes.size());
+ if (array == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return nullptr;
+ }
+ jint* arrayValues = env->GetIntArrayElements(array, 0);
+ int index = 0;
+ for (auto hdrOutputType : hdrOutputTypes) {
+ arrayValues[index++] = static_cast<jint>(hdrOutputType);
+ }
+ env->ReleaseIntArrayElements(array, arrayValues, 0);
+ return array;
+}
+
static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
ScopedLongArrayRW values(env, env->NewLongArray(displayIds.size()));
@@ -91,6 +144,10 @@
(void*)nativeGetPhysicalDisplayIds },
{"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
(void*)nativeGetPhysicalDisplayToken },
+ {"nativeSetHdrConversionMode", "(II[II)V",
+ (void*)nativeSetHdrConversionMode },
+ {"nativeGetSupportedHdrOutputTypes", "()[I",
+ (void*)nativeGetSupportedHdrOutputTypes },
// clang-format on
};
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 3453cbd..98e6b19 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -338,6 +338,12 @@
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notifyTvMessageEvent(
+ const AidlTvMessageEvent& event) {
+ // TODO: Implement this
+ return ::ndk::ScopedAStatus::ok();
+}
+
JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput)
: mIsHidl(false), mAidlTvInput(aidlTvInput) {}
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index 2697294..984407a 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -52,6 +52,7 @@
using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle;
using AidlTvInputDeviceInfo = ::aidl::android::hardware::tv::input::TvInputDeviceInfo;
using AidlTvInputEvent = ::aidl::android::hardware::tv::input::TvInputEvent;
+using AidlTvMessageEvent = ::aidl::android::hardware::tv::input::TvMessageEvent;
using AidlTvStreamConfig = ::aidl::android::hardware::tv::input::TvStreamConfig;
extern gTvInputHalClassInfoType gTvInputHalClassInfo;
@@ -131,6 +132,7 @@
public:
explicit TvInputCallback(JTvInputHal* hal);
::ndk::ScopedAStatus notify(const AidlTvInputEvent& event) override;
+ ::ndk::ScopedAStatus notifyTvMessageEvent(const AidlTvMessageEvent& event) override;
Return<void> notify(const HidlTvInputEvent& event) override;
private:
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 7f85347..97dbe04 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -117,6 +117,11 @@
<xs:element type="integer-array" name="screenOffBrightnessSensorValueToLux">
<xs:annotation name="final"/>
</xs:element>
+ <!-- The version of the Universal Stylus Initiative
+ (USI, https://universalstylus.org/) protocol supported by the display, if any. -->
+ <xs:element type="usiVersion" name="usiVersion">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -202,12 +207,15 @@
</xs:simpleType>
<xs:complexType name="thermalThrottling">
- <xs:complexType>
+ <xs:sequence>
<xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- </xs:complexType>
+ <xs:element type="brightnessThrottlingMap" name="concurrentDisplaysBrightnessThrottlingMap">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
</xs:complexType>
<xs:complexType name="brightnessThrottlingMap">
@@ -499,4 +507,15 @@
<xs:element name="item" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
+
+ <xs:complexType name="usiVersion">
+ <xs:element name="majorVersion" type="xs:nonNegativeInteger"
+ minOccurs="1" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="minorVersion" type="xs:nonNegativeInteger"
+ minOccurs="1" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:complexType>
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index cb08179..aba8a2c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -101,6 +101,7 @@
method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
+ method public final com.android.server.display.config.UsiVersion getUsiVersion();
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
method public final void setAmbientBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
method public final void setAmbientLightHorizonLong(java.math.BigInteger);
@@ -125,6 +126,7 @@
method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
+ method public final void setUsiVersion(com.android.server.display.config.UsiVersion);
}
public class DisplayQuirks {
@@ -237,7 +239,9 @@
public class ThermalThrottling {
ctor public ThermalThrottling();
method @NonNull public final com.android.server.display.config.BrightnessThrottlingMap getBrightnessThrottlingMap();
+ method public final com.android.server.display.config.BrightnessThrottlingMap getConcurrentDisplaysBrightnessThrottlingMap();
method public final void setBrightnessThrottlingMap(@NonNull com.android.server.display.config.BrightnessThrottlingMap);
+ method public final void setConcurrentDisplaysBrightnessThrottlingMap(com.android.server.display.config.BrightnessThrottlingMap);
}
public class ThresholdPoint {
@@ -261,6 +265,14 @@
method public final void setDarkeningThresholds(@NonNull com.android.server.display.config.BrightnessThresholds);
}
+ public class UsiVersion {
+ ctor public UsiVersion();
+ method public final java.math.BigInteger getMajorVersion();
+ method public final java.math.BigInteger getMinorVersion();
+ method public final void setMajorVersion(java.math.BigInteger);
+ method public final void setMinorVersion(java.math.BigInteger);
+ }
+
public class XmlParser {
ctor public XmlParser();
method public static com.android.server.display.config.DisplayConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index b7c5fc2..fbdcc44 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -17,11 +17,8 @@
package com.android.server.credentials;
import android.credentials.CredentialDescription;
-import android.credentials.IRegisterCredentialDescriptionCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
-import android.os.RemoteException;
import android.util.SparseArray;
import java.util.HashMap;
@@ -33,6 +30,7 @@
public class CredentialDescriptionRegistry {
private static final int MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS = 128;
+ private static final int MAX_ALLOWED_ENTRIES_PER_PROVIDER = 16;
private static SparseArray<CredentialDescriptionRegistry> sCredentialDescriptionSessionPerUser;
static {
@@ -53,48 +51,45 @@
}
private Map<String, Set<CredentialDescription>> mCredentialDescriptions;
+ private int mTotalDescriptionCount;
private CredentialDescriptionRegistry() {
this.mCredentialDescriptions = new HashMap<>();
+ this.mTotalDescriptionCount = 0;
}
/** Handle the given {@link RegisterCredentialDescriptionRequest} by creating
* the appropriate package name mapping. */
public void executeRegisterRequest(RegisterCredentialDescriptionRequest request,
- String callingPackageName,
- IRegisterCredentialDescriptionCallback callback) {
+ String callingPackageName) {
- if (!mCredentialDescriptions.containsKey(callingPackageName)
- && mCredentialDescriptions.size() <= MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS) {
+ if (!mCredentialDescriptions.containsKey(callingPackageName)) {
mCredentialDescriptions.put(callingPackageName, new HashSet<>());
}
- mCredentialDescriptions.get(callingPackageName)
- .addAll(request.getCredentialDescriptions());
-
- try {
- callback.onResponse();
- } catch (RemoteException e) {
- e.printStackTrace();
+ if (mTotalDescriptionCount <= MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS
+ && mCredentialDescriptions.get(callingPackageName).size()
+ <= MAX_ALLOWED_ENTRIES_PER_PROVIDER) {
+ Set<CredentialDescription> descriptions = request.getCredentialDescriptions();
+ int size = mCredentialDescriptions.get(callingPackageName).size();
+ mCredentialDescriptions.get(callingPackageName)
+ .addAll(descriptions);
+ mTotalDescriptionCount += size - mCredentialDescriptions.get(callingPackageName).size();
}
+
}
/** Handle the given {@link UnregisterCredentialDescriptionRequest} by creating
* the appropriate package name mapping. */
public void executeUnregisterRequest(
UnregisterCredentialDescriptionRequest request,
- String callingPackageName,
- IUnregisterCredentialDescriptionCallback callback) {
+ String callingPackageName) {
if (mCredentialDescriptions.containsKey(callingPackageName)) {
+ int size = mCredentialDescriptions.get(callingPackageName).size();
mCredentialDescriptions.get(callingPackageName)
- .remove(request.getCredentialDescription());
- }
-
- try {
- callback.onResponse();
- } catch (RemoteException e) {
- e.printStackTrace();
+ .removeAll(request.getCredentialDescriptions());
+ mTotalDescriptionCount -= size - mCredentialDescriptions.get(callingPackageName).size();
}
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 620b81b..bb26fa9 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -28,6 +28,7 @@
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialRequest;
+import android.credentials.CredentialDescription;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialOption;
import android.credentials.GetCredentialRequest;
@@ -36,9 +37,7 @@
import android.credentials.ICredentialManager;
import android.credentials.IGetCredentialCallback;
import android.credentials.IListEnabledProvidersCallback;
-import android.credentials.IRegisterCredentialDescriptionCallback;
import android.credentials.ISetEnabledProvidersCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
import android.credentials.ListEnabledProvidersResponse;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
@@ -274,8 +273,8 @@
request.getGetCredentialOptions().stream().map(
getCredentialOption -> getCredentialOption
.getCredentialRetrievalData()
- .getString(RegisterCredentialDescriptionRequest
- .FLATTENED_REQUEST_STRING_KEY))
+ .getString(GetCredentialOption
+ .FLATTENED_REQUEST))
.collect(Collectors.toSet());
// All requested credential descriptions based on the given request.
@@ -543,12 +542,10 @@
}
@Override
- public ICancellationSignal registerCredentialDescription(
- RegisterCredentialDescriptionRequest request,
- IRegisterCredentialDescriptionCallback callback, String callingPackage) {
+ public void registerCredentialDescription(
+ RegisterCredentialDescriptionRequest request, String callingPackage)
+ throws IllegalArgumentException , NonCredentialProviderCallerException {
Log.i(TAG, "registerCredentialDescription");
- ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-
List<CredentialProviderInfo> services =
CredentialProviderInfo.getAvailableServices(mContext,
@@ -557,31 +554,37 @@
List<String> providers = services.stream()
.map(credentialProviderInfo
-> credentialProviderInfo.getServiceInfo().packageName).toList();
+
if (!providers.contains(callingPackage)) {
- try {
- callback.onError("UNKNOWN",
- "Not an existing provider.");
- } catch (RemoteException e) {
- Log.i(
- TAG,
- "Issue invoking onError on IRegisterCredentialDescriptionCallback "
- + "callback: "
- + e.getMessage());
- }
+ throw new NonCredentialProviderCallerException(callingPackage);
+ }
+
+ List<CredentialProviderInfo> matchingService = services.stream().filter(
+ credentialProviderInfo ->
+ credentialProviderInfo.getServiceInfo()
+ .packageName.equals(callingPackage)).toList();
+
+ CredentialProviderInfo credentialProviderInfo = matchingService.get(0);
+
+ Set<String> supportedTypes = request.getCredentialDescriptions()
+ .stream().map(CredentialDescription::getType).filter(
+ credentialProviderInfo::hasCapability).collect(Collectors.toSet());
+
+ if (supportedTypes.size() != request.getCredentialDescriptions().size()) {
+ throw new IllegalArgumentException("CredentialProvider does not support one or more"
+ + "of the registered types. Check your XML entry.");
}
CredentialDescriptionRegistry session = CredentialDescriptionRegistry
.forUser(UserHandle.getCallingUserId());
- session.executeRegisterRequest(request, callingPackage, callback);
-
- return cancelTransport;
+ session.executeRegisterRequest(request, callingPackage);
}
@Override
- public ICancellationSignal unRegisterCredentialDescription(
- UnregisterCredentialDescriptionRequest request,
- IUnregisterCredentialDescriptionCallback callback, String callingPackage) {
+ public void unregisterCredentialDescription(
+ UnregisterCredentialDescriptionRequest request, String callingPackage)
+ throws IllegalArgumentException {
Log.i(TAG, "registerCredentialDescription");
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
@@ -594,24 +597,13 @@
-> credentialProviderInfo.getServiceInfo().packageName).toList();
if (!providers.contains(callingPackage)) {
- try {
- callback.onError("UNKNOWN",
- "Not an existing provider.");
- } catch (RemoteException e) {
- Log.i(
- TAG,
- "Issue invoking onError on IRegisterCredentialDescriptionCallback "
- + "callback: "
- + e.getMessage());
- }
+ throw new NonCredentialProviderCallerException(callingPackage);
}
CredentialDescriptionRegistry session = CredentialDescriptionRegistry
.forUser(UserHandle.getCallingUserId());
- session.executeUnregisterRequest(request, callingPackage, callback);
-
- return cancelTransport;
+ session.executeUnregisterRequest(request, callingPackage);
}
}
}
diff --git a/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java b/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java
new file mode 100644
index 0000000..383d0aa
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials;
+
+/**
+ * Thrown when the calling app is not a Credential Provider.
+ */
+public class NonCredentialProviderCallerException extends RuntimeException {
+
+ private static final String MESSAGE = " is not an existing Credential Provider.";
+
+ public NonCredentialProviderCallerException(String caller) {
+ super(caller + MESSAGE);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 4c42791..36cb898 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -174,6 +174,7 @@
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
private static final String ATTR_PACKAGE_POLICY_MODE = "package-policy-type";
+ private static final String TAG_CREDENTIAL_MANAGER_POLICY = "credential-manager-policy";
DeviceAdminInfo info;
@@ -332,6 +333,9 @@
// The package policy for Cross Profile Contacts Search
PackagePolicy mManagedProfileContactsAccess = null;
+ // The package policy for Credential Manager
+ PackagePolicy mCredentialManagerPolicy = null;
+
public String mAlwaysOnVpnPackage;
public boolean mAlwaysOnVpnLockdown;
boolean mCommonCriteriaMode;
@@ -647,6 +651,8 @@
mManagedProfileCallerIdAccess);
writePackagePolicy(out, TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY,
mManagedProfileContactsAccess);
+ writePackagePolicy(out, TAG_CREDENTIAL_MANAGER_POLICY,
+ mCredentialManagerPolicy);
if (mManagedSubscriptionsPolicy != null) {
out.startTag(null, TAG_MANAGED_SUBSCRIPTIONS_POLICY);
mManagedSubscriptionsPolicy.saveToXml(out);
@@ -958,6 +964,8 @@
mManagedProfileContactsAccess = readPackagePolicy(parser);
} else if (TAG_MANAGED_SUBSCRIPTIONS_POLICY.equals(tag)) {
mManagedSubscriptionsPolicy = ManagedSubscriptionsPolicy.readFromXml(parser);
+ } else if (TAG_CREDENTIAL_MANAGER_POLICY.equals(tag)) {
+ mCredentialManagerPolicy = readPackagePolicy(parser);
} else {
Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
@@ -1332,6 +1340,9 @@
dumpPackagePolicy(pw, "managedProfileContactsPolicy",
mManagedProfileContactsAccess);
+ dumpPackagePolicy(pw, "credentialManagerPolicy",
+ mCredentialManagerPolicy);
+
pw.print("isParent=");
pw.println(isParent);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
deleted file mode 100644
index 834f65f..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ /dev/null
@@ -1,209 +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.
- */
-package com.android.server.devicepolicy;
-
-import android.accounts.Account;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyDrawableResource;
-import android.app.admin.DevicePolicySafetyChecker;
-import android.app.admin.DevicePolicyStringResource;
-import android.app.admin.FullyManagedDeviceProvisioningParams;
-import android.app.admin.IDevicePolicyManager;
-import android.app.admin.ManagedProfileProvisioningParams;
-import android.app.admin.ParcelableResource;
-import android.content.ComponentName;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.server.SystemService;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Defines the required interface for IDevicePolicyManager implemenation.
- *
- * <p>The interface consists of public parts determined by {@link IDevicePolicyManager} and also
- * several package private methods required by internal infrastructure.
- *
- * <p>Whenever adding an AIDL method to {@link IDevicePolicyManager}, an empty override method
- * should be added here to avoid build breakage in downstream branches.
- */
-abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
-
- private static final String TAG = BaseIDevicePolicyManager.class.getSimpleName();
-
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
- *
- * @see {@link SystemService#onBootPhase}.
- */
- abstract void systemReady(int phase);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when a new user starts.
- *
- * @see {@link SystemService#onUserStarting}
- */
- abstract void handleStartUser(int userId);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being unlocked.
- *
- * @see {@link SystemService#onUserUnlocking}
- */
- abstract void handleUnlockUser(int userId);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} after a user is being unlocked.
- *
- * @see {@link SystemService#onUserUnlocked}
- */
- abstract void handleOnUserUnlocked(int userId);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being stopped.
- *
- * @see {@link SystemService#onUserStopping}
- */
- abstract void handleStopUser(int userId);
-
- /**
- * Sets the {@link DevicePolicySafetyChecker}.
- *
- * <p>Currently, it's called only by {@code SystemServer} on
- * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}
- */
- public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
- Slog.w(TAG, "setDevicePolicySafetyChecker() not implemented by " + getClass());
- }
-
- public void clearSystemUpdatePolicyFreezePeriodRecord() {
- }
-
- public boolean setKeyGrantForApp(ComponentName admin, String callerPackage, String alias,
- String packageName, boolean hasGrant) {
- return false;
- }
-
- public void setLocationEnabled(ComponentName who, boolean locationEnabled) {}
-
- public boolean isOrganizationOwnedDeviceWithManagedProfile() {
- return false;
- }
-
- public int getPersonalAppsSuspendedReasons(ComponentName admin) {
- return 0;
- }
-
- public void setPersonalAppsSuspended(ComponentName admin, boolean suspended) {
- }
-
- public void setManagedProfileMaximumTimeOff(ComponentName admin, long timeoutMs) {
- }
-
- public long getManagedProfileMaximumTimeOff(ComponentName admin) {
- return 0;
- }
-
- @Override
- public void acknowledgeDeviceCompliant() {}
-
- @Override
- public boolean isComplianceAcknowledgementRequired() {
- return false;
- }
-
- public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
- return false;
- }
-
- public String getEnrollmentSpecificId(String callerPackage) {
- return "";
- }
-
- public void setOrganizationIdForUser(
- @NonNull String callerPackage, @NonNull String enterpriseId, int userId) {}
-
- public UserHandle createAndProvisionManagedProfile(
- @NonNull ManagedProfileProvisioningParams provisioningParams, String callerPackage) {
- return null;
- }
-
- public void finalizeWorkProfileProvisioning(
- UserHandle managedProfileUser, Account migratedAccount) {
-
- }
-
- public void provisionFullyManagedDevice(
- FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
- }
-
- @Override
- public void setDeviceOwnerType(@NonNull ComponentName admin, int deviceOwnerType) {
- }
-
- @Override
- public int getDeviceOwnerType(@NonNull ComponentName admin) {
- return 0;
- }
-
- public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {}
-
- public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
- return false;
- }
-
- @Override
- public boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant) {
- return false;
- }
-
- @Override
- public boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias) {
- return false;
- }
-
- @Override
- public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables){}
-
- @Override
- public void resetDrawables(@NonNull List<String> drawableIds){}
-
- @Override
- public ParcelableResource getDrawable(
- String drawableId, String drawableStyle, String drawableSource) {
- return null;
- }
-
- @Override
- public void setStrings(@NonNull List<DevicePolicyStringResource> strings){}
-
- @Override
- public void resetStrings(@NonNull List<String> stringIds){}
-
- @Override
- public ParcelableResource getString(String stringId) {
- return null;
- }
-
- @Override
- public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
- return false;
- }
-
- @Override
- public List<UserHandle> getPolicyManagedProfiles(UserHandle userHandle) {
- return Collections.emptyList();
- }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 304d148..4351bc1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -20,11 +20,12 @@
import android.app.admin.DevicePolicyManager;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
-import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* Implementation of {@link DevicePolicyCache}, to which {@link DevicePolicyManagerService} pushes
* policies.
@@ -51,20 +52,13 @@
@GuardedBy("mLock")
private final SparseIntArray mPermissionPolicy = new SparseIntArray();
- /** Maps to {@code ActiveAdmin.mAdminCanGrantSensorsPermissions}.
- *
- * <p>For users affiliated with the device, they inherit the policy from {@code DO} so
- * it will map to the {@code DO}'s policy. Otherwise it will map to the admin of the requesting
- * user.
- */
- @GuardedBy("mLock")
- private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray();
+ /** Maps to {@code ActiveAdmin.mAdminCanGrantSensorsPermissions}. */
+ private final AtomicBoolean mCanGrantSensorsPermissions = new AtomicBoolean(false);
public void onUserRemoved(int userHandle) {
synchronized (mLock) {
mPasswordQuality.delete(userHandle);
mPermissionPolicy.delete(userHandle);
- mCanGrantSensorsPermissions.delete(userHandle);
}
}
@@ -119,28 +113,25 @@
}
@Override
- public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userId) {
- synchronized (mLock) {
- return mCanGrantSensorsPermissions.get(userId, false);
- }
+ public boolean canAdminGrantSensorsPermissions() {
+ return mCanGrantSensorsPermissions.get();
}
- /** Sets ahmin control over permission grants for user. */
- public void setAdminCanGrantSensorsPermissions(@UserIdInt int userId, boolean canGrant) {
- synchronized (mLock) {
- mCanGrantSensorsPermissions.put(userId, canGrant);
- }
+ /** Sets admin control over permission grants. */
+ public void setAdminCanGrantSensorsPermissions(boolean canGrant) {
+ mCanGrantSensorsPermissions.set(canGrant);
}
/** Dump content */
public void dump(IndentingPrintWriter pw) {
- pw.println("Device policy cache:");
- pw.increaseIndent();
- pw.println("Screen capture disallowed user: " + mScreenCaptureDisallowedUser);
- pw.println("Password quality: " + mPasswordQuality.toString());
- pw.println("Permission policy: " + mPermissionPolicy.toString());
- pw.println("Admin can grant sensors permission: "
- + mCanGrantSensorsPermissions.toString());
- pw.decreaseIndent();
+ synchronized (mLock) {
+ pw.println("Device policy cache:");
+ pw.increaseIndent();
+ pw.println("Screen capture disallowed user: " + mScreenCaptureDisallowedUser);
+ pw.println("Password quality: " + mPasswordQuality);
+ pw.println("Permission policy: " + mPermissionPolicy);
+ pw.println("Admin can grant sensors permission: " + mCanGrantSensorsPermissions.get());
+ pw.decreaseIndent();
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0963e3b..c9c9c55 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -221,6 +221,7 @@
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.FullyManagedDeviceProvisioningParams;
+import android.app.admin.IDevicePolicyManager;
import android.app.admin.ManagedProfileProvisioningParams;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.NetworkEvent;
@@ -395,6 +396,8 @@
import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.DefaultCrossProfileIntentFilter;
+import com.android.server.pm.DefaultCrossProfileIntentFiltersUtils;
import com.android.server.pm.RestrictionsSet;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
@@ -442,7 +445,7 @@
/**
* Implementation of the device policy APIs.
*/
-public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
protected static final String LOG_TAG = "DevicePolicyManager";
@@ -739,6 +742,10 @@
private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
+ private static final String ENABLE_WORK_PROFILE_TELEPHONY_FLAG =
+ "enable_work_profile_telephony";
+ private static final boolean DEFAULT_WORK_PROFILE_TELEPHONY_FLAG = false;
+
// TODO(b/261999445) remove the flag after rollout.
private static final String HEADLESS_FLAG = "headless";
private static final boolean DEFAULT_HEADLESS_FLAG = true;
@@ -920,23 +927,24 @@
private final ArrayList<Object> mPendingUserCreatedCallbackTokens = new ArrayList<>();
public static final class Lifecycle extends SystemService {
- private BaseIDevicePolicyManager mService;
+ private DevicePolicyManagerService mService;
public Lifecycle(Context context) {
super(context);
String dpmsClassName = context.getResources()
.getString(R.string.config_deviceSpecificDevicePolicyManagerService);
if (TextUtils.isEmpty(dpmsClassName)) {
- dpmsClassName = DevicePolicyManagerService.class.getName();
- }
- try {
- Class<?> serviceClass = Class.forName(dpmsClassName);
- Constructor<?> constructor = serviceClass.getConstructor(Context.class);
- mService = (BaseIDevicePolicyManager) constructor.newInstance(context);
- } catch (Exception e) {
- throw new IllegalStateException(
- "Failed to instantiate DevicePolicyManagerService with class name: "
- + dpmsClassName, e);
+ mService = new DevicePolicyManagerService(context);
+ } else {
+ try {
+ Class<?> serviceClass = Class.forName(dpmsClassName);
+ Constructor<?> constructor = serviceClass.getConstructor(Context.class);
+ mService = (DevicePolicyManagerService) constructor.newInstance(context);
+ } catch (Exception e) {
+ throw new IllegalStateException(
+ "Failed to instantiate DevicePolicyManagerService with class name: "
+ + dpmsClassName, e);
+ }
}
}
@@ -1348,7 +1356,6 @@
}
}
- @Override
public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
CallerIdentity callerIdentity = getCallerIdentity();
Preconditions.checkCallAuthorization(mIsAutomotive || isAdb(callerIdentity), "can only set "
@@ -1620,8 +1627,9 @@
return LocalServices.getService(LockSettingsInternal.class);
}
- CrossProfileApps getCrossProfileApps() {
- return mContext.getSystemService(CrossProfileApps.class);
+ CrossProfileApps getCrossProfileApps(@UserIdInt int userId) {
+ return mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0)
+ .getSystemService(CrossProfileApps.class);
}
boolean hasUserSetupCompleted(DevicePolicyData userData) {
@@ -2407,22 +2415,23 @@
return;
}
- final UserHandle doUserHandle = UserHandle.of(doUserId);
-
- // Based on CDD : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support,
- // creation of clone profile is not allowed in case device owner is set.
- // Enforcing this restriction on setting up of device owner.
- if (!mUserManager.hasUserRestriction(
- UserManager.DISALLOW_ADD_CLONE_PROFILE, doUserHandle)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
- doUserHandle);
- }
- // Creation of managed profile is restricted in case device owner is set, enforcing this
- // restriction by setting user level restriction at time of device owner setup.
- if (!mUserManager.hasUserRestriction(
- UserManager.DISALLOW_ADD_MANAGED_PROFILE, doUserHandle)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
- doUserHandle);
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ UserHandle userHandle = userInfo.getUserHandle();
+ // Based on CDD : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support,
+ // creation of clone profile is not allowed in case device owner is set.
+ // Enforcing this restriction on setting up of device owner.
+ if (!mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_ADD_CLONE_PROFILE, userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
+ userHandle);
+ }
+ // Creation of managed profile is restricted in case device owner is set, enforcing this
+ // restriction by setting user level restriction at time of device owner setup.
+ if (!mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_ADD_MANAGED_PROFILE, userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+ userHandle);
+ }
}
}
@@ -3090,7 +3099,6 @@
}
@VisibleForTesting
- @Override
void systemReady(int phase) {
if (!mHasFeature) {
return;
@@ -3100,7 +3108,9 @@
onLockSettingsReady();
loadAdminDataAsync();
mOwners.systemReady();
- applyManagedSubscriptionsPolicyIfRequired();
+ if (isWorkProfileTelephonyFlagEnabled()) {
+ applyManagedSubscriptionsPolicyIfRequired();
+ }
break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
synchronized (getLockObject()) {
@@ -3126,8 +3136,9 @@
unregisterOnSubscriptionsChangedListener();
int policyType = getManagedSubscriptionsPolicy().getPolicyType();
if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS) {
+ final int parentUserId = getProfileParentId(copeProfileUserId);
// By default, assign all current and future subs to system user on COPE devices.
- registerListenerToAssignSubscriptionsToUser(UserHandle.USER_SYSTEM);
+ registerListenerToAssignSubscriptionsToUser(parentUserId);
} else if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
// Add listener to assign all current and future subs to managed profile.
registerListenerToAssignSubscriptionsToUser(copeProfileUserId);
@@ -3289,7 +3300,6 @@
}
}
- @Override
void handleStartUser(int userId) {
synchronized (getLockObject()) {
pushScreenCapturePolicy(userId);
@@ -3337,7 +3347,6 @@
targetUserId, protectedPackages));
}
- @Override
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
if (isCoexistenceFlagEnabled()) {
@@ -3345,12 +3354,10 @@
}
}
- @Override
void handleOnUserUnlocked(int userId) {
showNewUserDisclaimerIfNecessary(userId);
}
- @Override
void handleStopUser(int userId) {
updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
@@ -3762,21 +3769,56 @@
}
private void clearDeviceOwnerUserRestriction(UserHandle userHandle) {
- // ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the original state
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, userHandle);
- }
- // When a device owner is set, the system automatically restricts adding a managed profile.
- // Remove this restriction when the device owner is cleared.
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, userHandle)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
- userHandle);
- }
- // When a device owner is set, the system automatically restricts adding a clone profile.
- // Remove this restriction when the device owner is cleared.
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, userHandle)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, false,
- userHandle);
+ if (isHeadlessFlagEnabled()) {
+ for (int userId : mUserManagerInternal.getUserIds()) {
+ UserHandle user = UserHandle.of(userId);
+ // ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the
+ // original state
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, user)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
+ false, user);
+ }
+ // When a device owner is set, the system automatically restricts adding a
+ // managed profile.
+ // Remove this restriction when the device owner is cleared.
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ user)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ false,
+ user);
+ }
+ // When a device owner is set, the system automatically restricts adding a
+ // clone profile.
+ // Remove this restriction when the device owner is cleared.
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, user)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+ false, user);
+ }
+ }
+ } else {
+ // ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the original state
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
+ userHandle);
+ }
+ // When a device owner is set, the system automatically restricts adding a
+ // managed profile.
+ // Remove this restriction when the device owner is cleared.
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ false,
+ userHandle);
+ }
+ // When a device owner is set, the system automatically restricts adding a clone
+ // profile.
+ // Remove this restriction when the device owner is cleared.
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+ userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+ false,
+ userHandle);
+ }
}
}
@@ -7018,8 +7060,10 @@
}
mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
- clearManagedSubscriptionsPolicy();
-
+ if (isWorkProfileTelephonyFlagEnabled()) {
+ clearManagedSubscriptionsPolicy();
+ updateTelephonyCrossProfileIntentFilters(parentId, UserHandle.USER_NULL, false);
+ }
Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
}
@@ -7035,6 +7079,33 @@
}
}
+ private void updateTelephonyCrossProfileIntentFilters(int parentUserId, int profileUserId,
+ boolean enableWorkTelephony) {
+ try {
+ String packageName = mContext.getOpPackageName();
+ if (enableWorkTelephony) {
+ // Reset call/sms cross profile intent filters to be handled by managed profile.
+ for (DefaultCrossProfileIntentFilter filter :
+ DefaultCrossProfileIntentFiltersUtils
+ .getDefaultManagedProfileTelephonyFilters()) {
+ IntentFilter intentFilter = filter.filter.getIntentFilter();
+ if (!mIPackageManager.removeCrossProfileIntentFilter(intentFilter, packageName,
+ profileUserId, parentUserId, filter.flags)) {
+ Slogf.w(LOG_TAG,
+ "Failed to remove cross-profile intent filter: " + intentFilter);
+ }
+
+ mIPackageManager.addCrossProfileIntentFilter(intentFilter, packageName,
+ parentUserId, profileUserId, PackageManager.SKIP_CURRENT_PROFILE);
+ }
+ } else {
+ mIPackageManager.clearCrossProfileIntentFilters(parentUserId, packageName);
+ }
+ } catch (RemoteException re) {
+ Slogf.wtf(LOG_TAG, "Error updating telephony cross profile intent filters", re);
+ }
+ }
+
/**
* @param factoryReset null: legacy behaviour, false: attempt to remove user, true: attempt to
* factory reset
@@ -8639,7 +8710,7 @@
// hard-coded default value setting.
if (isAdb(caller)) {
activeAdmin.mAdminCanGrantSensorsPermissions = true;
- mPolicyCache.setAdminCanGrantSensorsPermissions(userId, true);
+ mPolicyCache.setAdminCanGrantSensorsPermissions(true);
saveSettingsLocked(userId);
}
@@ -8651,14 +8722,31 @@
// profile, such that the admin on that managed profile has extended management
// capabilities that can affect the entire device (but not access private data
// on the primary profile).
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
- UserHandle.of(userId));
- // Restrict adding a clone profile when a device owner is set on the device.
- // That is to prevent the co-existence of a clone profile and a device owner
- // on the same device.
- // CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
- UserHandle.of(userId));
+ if (isHeadlessFlagEnabled()) {
+ for (int u : mUserManagerInternal.getUserIds()) {
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+ UserHandle.of(u));
+ // Restrict adding a clone profile when a device owner is set on the device.
+ // That is to prevent the co-existence of a clone profile and a device owner
+ // on the same device.
+ // CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+ true,
+ UserHandle.of(u));
+ }
+ } else {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ true,
+ UserHandle.of(userId));
+ // Restrict adding a clone profile when a device owner is set on the device.
+ // That is to prevent the co-existence of a clone profile and a device owner
+ // on the same device.
+ // CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+ true,
+ UserHandle.of(userId));
+ }
// TODO Send to system too?
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
});
@@ -9285,7 +9373,7 @@
}
@Override
- public int getUserProvisioningState() {
+ public int getUserProvisioningState(int userHandle) {
if (!mHasFeature) {
return DevicePolicyManager.STATE_USER_UNMANAGED;
}
@@ -9293,10 +9381,11 @@
Preconditions.checkCallAuthorization(canManageUsers(caller)
|| hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- return getUserProvisioningState(caller.getUserId());
- }
+ if (userHandle != caller.getUserId()) {
+ Preconditions.checkCallAuthorization(canManageUsers(caller)
+ || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
+ }
- private int getUserProvisioningState(int userHandle) {
return getUserData(userHandle).mUserProvisioningState;
}
@@ -9307,7 +9396,6 @@
+ userId);
return;
}
-
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
@@ -10132,6 +10220,9 @@
synchronized (mSubscriptionsChangedListenerLock) {
pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
}
+ pw.println(
+ "Flag enable_work_profile_telephony : " + isWorkProfileTelephonyFlagEnabled());
+
mHandler.post(() -> handleDump(pw));
dumpResources(pw);
}
@@ -14310,6 +14401,46 @@
}
@Override
+ public void setCredentialManagerPolicy(PackagePolicy policy) {
+ if (!mHasFeature) {
+ return;
+ }
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(canWriteCredentialManagerPolicy(caller));
+
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ if (Objects.equals(admin.mCredentialManagerPolicy, policy)) {
+ return;
+ }
+
+ admin.mCredentialManagerPolicy = policy;
+ saveSettingsLocked(caller.getUserId());
+ }
+ }
+
+ private boolean canWriteCredentialManagerPolicy(CallerIdentity caller) {
+ return (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))
+ || isDefaultDeviceOwner(caller)
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ }
+
+ @Override
+ public PackagePolicy getCredentialManagerPolicy() {
+ if (!mHasFeature) {
+ return null;
+ }
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ canWriteCredentialManagerPolicy(caller) || canQueryAdminPolicy(caller));
+
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ return (admin != null) ? admin.mCredentialManagerPolicy : null;
+ }
+ }
+
+ @Override
public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) {
if (policy != null) {
// throws exception if policy type is invalid
@@ -14621,7 +14752,7 @@
AdminPermissionControlParams permissionParams =
new AdminPermissionControlParams(packageName, permission,
grantState,
- canAdminGrantSensorsPermissionsForUser(caller.getUserId()));
+ canAdminGrantSensorsPermissions());
mInjector.getPermissionControllerManager(caller.getUserHandle())
.setRuntimePermissionGrantStateByDeviceAdmin(
caller.getPackageName(),
@@ -18599,7 +18730,7 @@
"Error creating profile, createProfileForUserEvenWhenDisallowed "
+ "returned null.");
}
- resetInteractAcrossProfilesAppOps();
+ resetInteractAcrossProfilesAppOps(caller.getUserId());
logEventDuration(
DevicePolicyEnums.PLATFORM_PROVISIONING_CREATE_PROFILE_MS,
startTime,
@@ -18751,37 +18882,37 @@
return caller.getPackageName().equals(devicePolicyManagementRoleHolderPackageName);
}
- private void resetInteractAcrossProfilesAppOps() {
- mInjector.getCrossProfileApps().clearInteractAcrossProfilesAppOps();
- pregrantDefaultInteractAcrossProfilesAppOps();
+ private void resetInteractAcrossProfilesAppOps(@UserIdInt int userId) {
+ mInjector.getCrossProfileApps(userId).clearInteractAcrossProfilesAppOps();
+ pregrantDefaultInteractAcrossProfilesAppOps(userId);
}
- private void pregrantDefaultInteractAcrossProfilesAppOps() {
+ private void pregrantDefaultInteractAcrossProfilesAppOps(@UserIdInt int userId) {
final String op =
AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
- for (String packageName : getConfigurableDefaultCrossProfilePackages()) {
- if (appOpIsChangedFromDefault(op, packageName)) {
+ for (String packageName : getConfigurableDefaultCrossProfilePackages(userId)) {
+ if (!appOpIsDefaultOrAllowed(userId, op, packageName)) {
continue;
}
- mInjector.getCrossProfileApps().setInteractAcrossProfilesAppOp(
+ mInjector.getCrossProfileApps(userId).setInteractAcrossProfilesAppOp(
packageName, MODE_ALLOWED);
}
}
- private Set<String> getConfigurableDefaultCrossProfilePackages() {
+ private Set<String> getConfigurableDefaultCrossProfilePackages(@UserIdInt int userId) {
List<String> defaultPackages = getDefaultCrossProfilePackages();
return defaultPackages.stream().filter(
- mInjector.getCrossProfileApps()::canConfigureInteractAcrossProfiles).collect(
+ mInjector.getCrossProfileApps(userId)::canConfigureInteractAcrossProfiles).collect(
Collectors.toSet());
}
- private boolean appOpIsChangedFromDefault(String op, String packageName) {
+ private boolean appOpIsDefaultOrAllowed(@UserIdInt int userId, String op, String packageName) {
try {
- final int uid = mContext.getPackageManager().getPackageUid(
- packageName, /* flags= */ 0);
- return mInjector.getAppOpsManager().unsafeCheckOpNoThrow(
- op, uid, packageName)
- != AppOpsManager.MODE_DEFAULT;
+ final int uid = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0).
+ getPackageManager().getPackageUid(packageName, /* flags= */ 0);
+ int mode = mInjector.getAppOpsManager().unsafeCheckOpNoThrow(
+ op, uid, packageName);
+ return mode == MODE_ALLOWED || mode == MODE_DEFAULT;
} catch (NameNotFoundException e) {
return false;
}
@@ -19217,7 +19348,7 @@
"May only be set on a the user of a device owner.");
owner.mAdminCanGrantSensorsPermissions = canGrant;
- mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+ mPolicyCache.setAdminCanGrantSensorsPermissions(canGrant);
saveSettingsLocked(userId);
}
}
@@ -19232,6 +19363,7 @@
mInjector.settingsGlobalPutStringForUser(
Settings.Global.DEVICE_DEMO_MODE, Integer.toString(/* value= */ 1), userId);
}
+
setUserProvisioningState(STATE_USER_SETUP_FINALIZED, userId);
}
@@ -19247,7 +19379,7 @@
owner = getDeviceOrProfileOwnerAdminLocked(userId);
}
boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
- mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+ mPolicyCache.setAdminCanGrantSensorsPermissions(canGrant);
}
}
@@ -19292,12 +19424,12 @@
}
@Override
- public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+ public boolean canAdminGrantSensorsPermissions() {
if (!mHasFeature) {
return false;
}
- return mPolicyCache.canAdminGrantSensorsPermissionsForUser(userId);
+ return mPolicyCache.canAdminGrantSensorsPermissions();
}
@Override
@@ -20104,6 +20236,13 @@
DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
}
+ private static boolean isWorkProfileTelephonyFlagEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_DEVICE_POLICY_MANAGER,
+ ENABLE_WORK_PROFILE_TELEPHONY_FLAG,
+ DEFAULT_WORK_PROFILE_TELEPHONY_FLAG);
+ }
+
@Override
public void setMtePolicy(int flags) {
final Set<Integer> allowedModes =
@@ -20184,10 +20323,12 @@
@Override
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
- synchronized (getLockObject()) {
- ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
- if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
- return admin.mManagedSubscriptionsPolicy;
+ if (isWorkProfileTelephonyFlagEnabled()) {
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
+ if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
+ return admin.mManagedSubscriptionsPolicy;
+ }
}
}
return new ManagedSubscriptionsPolicy(
@@ -20196,9 +20337,13 @@
@Override
public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
+ if (!isWorkProfileTelephonyFlagEnabled()) {
+ throw new UnsupportedOperationException("This api is not enabled");
+ }
CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller),
- "This policy can only be set by a profile owner on an organization-owned device.");
+ "This policy can only be set by a profile owner on an organization-owned "
+ + "device.");
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
@@ -20226,6 +20371,7 @@
try {
int parentUserId = getProfileParentId(caller.getUserId());
installOemDefaultDialerAndMessagesApp(parentUserId, caller.getUserId());
+ updateTelephonyCrossProfileIntentFilters(parentUserId, caller.getUserId(), true);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -20316,4 +20462,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index 9fe090a..3976a70 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -36,5 +36,10 @@
}
]
}
+ ],
+ "kernel-presubmit": [
+ {
+ "name": "CtsIncrementalInstallHostTestCases"
+ }
]
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b1cc306..a15c6d2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1021,9 +1021,7 @@
Runnable runnable = new Runnable() {
@Override
public void run() {
- synchronized (this) {
- ShutdownThread.rebootOrShutdown(null, reboot, reason);
- }
+ ShutdownThread.rebootOrShutdown(null, reboot, reason);
}
};
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index 730cac9..0962b0d 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -146,6 +146,7 @@
addPermissions(packageState, changedPermissionNames)
trimPermissions(packageState.packageName, changedPermissionNames)
trimPermissionStates(packageState.appId)
+ revokePermissionsOnPackageUpdate(packageState.appId)
}
changedPermissionNames.forEachIndexed { _, permissionName ->
evaluatePermissionStateForAllPackages(permissionName, null)
@@ -175,10 +176,10 @@
adoptPermissions(packageState, changedPermissionNames)
addPermissionGroups(packageState)
addPermissions(packageState, changedPermissionNames)
- // TODO: revokeStoragePermissionsIfScopeExpandedInternal()
// TODO: revokeSystemAlertWindowIfUpgradedPast23()
trimPermissions(packageState.packageName, changedPermissionNames)
trimPermissionStates(packageState.appId)
+ revokePermissionsOnPackageUpdate(packageState.appId)
changedPermissionNames.forEachIndexed { _, permissionName ->
evaluatePermissionStateForAllPackages(permissionName, null)
}
@@ -602,6 +603,40 @@
}
}
+ private fun MutateStateScope.revokePermissionsOnPackageUpdate(appId: Int) {
+ // If the app is updated, and has scoped storage permissions, then it is possible that the
+ // app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ newState.userStates.forEachIndexed { _, userId, userState ->
+ userState.uidPermissionFlags[appId]?.forEachReversedIndexed {
+ _, permissionName, oldFlags ->
+ if (permissionName !in STORAGE_AND_MEDIA_PERMISSIONS || oldFlags == 0) {
+ return@forEachReversedIndexed
+ }
+ val oldTargetSdkVersion = getAppIdTargetSdkVersion(appId, permissionName, oldState)
+ val newTargetSdkVersion = getAppIdTargetSdkVersion(appId, permissionName, newState)
+ val isTargetSdkVersionDowngraded = oldTargetSdkVersion >= Build.VERSION_CODES.Q &&
+ newTargetSdkVersion < Build.VERSION_CODES.Q
+ val isTargetSdkVersionUpgraded = oldTargetSdkVersion < Build.VERSION_CODES.Q &&
+ newTargetSdkVersion >= Build.VERSION_CODES.Q
+ val oldIsRequestLegacyExternalStorage = anyPackageInAppId(appId, oldState) {
+ it.androidPackage!!.isRequestLegacyExternalStorage
+ }
+ val newIsRequestLegacyExternalStorage = anyPackageInAppId(appId, newState) {
+ it.androidPackage!!.isRequestLegacyExternalStorage
+ }
+ val isNewlyRequestingLegacyExternalStorage = !isTargetSdkVersionUpgraded &&
+ !oldIsRequestLegacyExternalStorage && newIsRequestLegacyExternalStorage
+ if ((isNewlyRequestingLegacyExternalStorage || isTargetSdkVersionDowngraded) &&
+ oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED)) {
+ val newFlags = oldFlags andInv (
+ PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK
+ )
+ setPermissionFlags(appId, userId, permissionName, newFlags)
+ }
+ }
+ }
+ }
+
private fun MutateStateScope.evaluatePermissionStateForAllPackages(
permissionName: String,
installedPackageState: PackageState?
@@ -1002,9 +1037,13 @@
}
}
- private fun MutateStateScope.getAppIdTargetSdkVersion(appId: Int, permissionName: String): Int {
+ private fun MutateStateScope.getAppIdTargetSdkVersion(
+ appId: Int,
+ permissionName: String,
+ state: AccessState = newState
+ ): Int {
var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
- forEachPackageInAppId(appId) { packageState ->
+ forEachPackageInAppId(appId, state) { packageState ->
val androidPackage = packageState.androidPackage!!
if (permissionName in androidPackage.requestedPermissions) {
targetSdkVersion = targetSdkVersion.coerceAtMost(androidPackage.targetSdkVersion)
@@ -1356,6 +1395,16 @@
Manifest.permission.POST_NOTIFICATIONS
)
+ private val STORAGE_AND_MEDIA_PERMISSIONS = indexedSetOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ )
+
/**
* Mask for all permission flags that can be set by the user
*/
diff --git a/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
similarity index 77%
rename from services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
rename to services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
index e5a1cfe..4949091 100644
--- a/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
@@ -26,7 +26,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.NtpTrustedTime;
-import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
import org.junit.Before;
import org.junit.Test;
@@ -41,16 +41,16 @@
import java.util.concurrent.TimeUnit;
/**
- * Unit tests for {@link NtpTimeHelper}.
+ * Unit tests for {@link NtpNetworkTimeHelper}.
*/
@RunWith(RobolectricTestRunner.class)
@Presubmit
-public class NtpTimeHelperTest {
+public class NtpNetworkTimeHelperTest {
private static final long MOCK_NTP_TIME = 1519930775453L;
@Mock
private NtpTrustedTime mMockNtpTrustedTime;
- private NtpTimeHelper mNtpTimeHelper;
+ private NtpNetworkTimeHelper mNtpNetworkTimeHelper;
private CountDownLatch mCountDownLatch;
/**
@@ -60,12 +60,12 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mCountDownLatch = new CountDownLatch(1);
- InjectNtpTimeCallback callback =
+ InjectTimeCallback callback =
(time, timeReference, uncertainty) -> {
assertThat(time).isEqualTo(MOCK_NTP_TIME);
mCountDownLatch.countDown();
};
- mNtpTimeHelper = new NtpTimeHelper(RuntimeEnvironment.application,
+ mNtpNetworkTimeHelper = new NtpNetworkTimeHelper(RuntimeEnvironment.application,
Looper.myLooper(),
callback, mMockNtpTrustedTime);
}
@@ -74,13 +74,13 @@
* Verify that cached time is returned if cached age is low.
*/
@Test
- public void handleInjectNtpTime_cachedAgeLow_injectTime() throws InterruptedException {
+ public void demandUtcTimeInjection_cachedAgeLow_injectTime() throws InterruptedException {
NtpTrustedTime.TimeResult result = mock(NtpTrustedTime.TimeResult.class);
- doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
+ doReturn(NtpNetworkTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
doReturn(MOCK_NTP_TIME).when(result).getTimeMillis();
doReturn(result).when(mMockNtpTrustedTime).getCachedTimeResult();
- mNtpTimeHelper.retrieveAndInjectNtpTime();
+ mNtpNetworkTimeHelper.demandUtcTimeInjection();
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
@@ -90,14 +90,14 @@
* Verify that failed inject time and delayed inject time are handled properly.
*/
@Test
- public void handleInjectNtpTime_injectTimeFailed_injectTimeDelayed()
+ public void demandUtcTimeInjection_injectTimeFailed_injectTimeDelayed()
throws InterruptedException {
NtpTrustedTime.TimeResult result1 = mock(NtpTrustedTime.TimeResult.class);
- doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
+ doReturn(NtpNetworkTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
doReturn(result1).when(mMockNtpTrustedTime).getCachedTimeResult();
doReturn(false).when(mMockNtpTrustedTime).forceRefresh();
- mNtpTimeHelper.retrieveAndInjectNtpTime();
+ mNtpNetworkTimeHelper.demandUtcTimeInjection();
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse();
@@ -106,15 +106,15 @@
doReturn(1L).when(result2).getAgeMillis();
doReturn(MOCK_NTP_TIME).when(result2).getTimeMillis();
doReturn(result2).when(mMockNtpTrustedTime).getCachedTimeResult();
- SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
+ SystemClock.sleep(NtpNetworkTimeHelper.RETRY_INTERVAL);
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
}
/**
- * Since a thread is created in {@link NtpTimeHelper#retrieveAndInjectNtpTime} and the task to
- * be verified is posted in the thread, we have to wait for the task to be posted and then it
+ * Since a thread is created in {@link NtpNetworkTimeHelper#demandUtcTimeInjection} and the task
+ * to be verified is posted in the thread, we have to wait for the task to be posted and then it
* can be run.
*/
private void waitForTasksToBePostedOnHandlerAndRunThem() throws InterruptedException {
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 3ef8586a..d128e68 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -267,7 +267,7 @@
Manifest.permission.INTERACT_ACROSS_USERS,
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
try {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
fail();
} catch (SecurityException expected) {}
@@ -280,7 +280,7 @@
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
grantPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES);
try {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
fail();
} catch (SecurityException expected) {}
@@ -288,7 +288,7 @@
@Test
public void setInteractAcrossProfilesAppOp_setsAppOp() {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
}
@@ -302,7 +302,7 @@
Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
Manifest.permission.INTERACT_ACROSS_USERS);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
@@ -316,7 +316,7 @@
Manifest.permission.UPDATE_APP_OPS_STATS,
Manifest.permission.INTERACT_ACROSS_USERS);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
@@ -326,7 +326,7 @@
public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() {
denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
}
@@ -335,28 +335,28 @@
public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() {
denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
}
@Test
public void setInteractAcrossProfilesAppOp_setsAppOpOnOtherProfile() {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(getCrossProfileAppOp(WORK_PROFILE_UID)).isEqualTo(MODE_ALLOWED);
}
@Test
public void setInteractAcrossProfilesAppOp_sendsBroadcast() {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isTrue();
}
@Test
public void setInteractAcrossProfilesAppOp_sendsBroadcastToOtherProfile() {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(WORK_PROFILE_USER_ID))
.isTrue();
@@ -364,7 +364,7 @@
@Test
public void setInteractAcrossProfilesAppOp_doesNotSendBroadcastToProfileWithoutPackage() {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(
OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID))
@@ -374,7 +374,7 @@
@Test
public void setInteractAcrossProfilesAppOp_toSameAsCurrent_doesNotSendBroadcast() {
explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse();
}
@@ -382,7 +382,7 @@
@Test
public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSet() {
mockCrossProfileAppNotWhitelisted();
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(getCrossProfileAppOp()).isNotEqualTo(MODE_ALLOWED);
}
@@ -390,7 +390,7 @@
@Test
public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSendBroadcast() {
mockCrossProfileAppNotWhitelisted();
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse();
}
@@ -398,7 +398,7 @@
@Test
public void setInteractAcrossProfilesAppOp_withoutCrossProfileAttribute_manifestReceiversDoNotGetBroadcast() {
declareCrossProfileAttributeOnCrossProfileApp(false);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isFalse();
}
@@ -406,14 +406,14 @@
@Test
public void setInteractAcrossProfilesAppOp_withCrossProfileAttribute_manifestReceiversGetBroadcast() {
declareCrossProfileAttributeOnCrossProfileApp(true);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isTrue();
}
@Test
public void setInteractAcrossProfilesAppOp_toAllowed_doesNotKillApp() {
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
assertThat(mKilledUids).isEmpty();
}
@@ -421,10 +421,10 @@
@Test
public void setInteractAcrossProfilesAppOp_toDisallowed_killsAppsInBothProfiles() {
shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
- mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+ mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
CROSS_PROFILE_APP_PACKAGE_NAME, MODE_DEFAULT);
assertThat(mKilledUids).contains(WORK_PROFILE_UID);
@@ -463,7 +463,8 @@
public void canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse() {
mockUninstallCrossProfileAppFromWorkProfile();
assertThat(mCrossProfileAppsServiceImpl
- .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isFalse();
}
@@ -482,7 +483,8 @@
throws Exception {
mockCrossProfileAppDoesNotRequestInteractAcrossProfiles();
assertThat(mCrossProfileAppsServiceImpl
- .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isFalse();
}
@@ -496,14 +498,16 @@
public void canConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsFalse() {
mockCrossProfileAppNotWhitelisted();
assertThat(mCrossProfileAppsServiceImpl
- .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isFalse();
}
@Test
public void canConfigureInteractAcrossProfiles_returnsTrue() {
assertThat(mCrossProfileAppsServiceImpl
- .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isTrue();
}
@@ -511,7 +515,8 @@
public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() {
mockUninstallCrossProfileAppFromWorkProfile();
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isTrue();
}
@@ -520,7 +525,8 @@
throws Exception {
mockCrossProfileAppDoesNotRequestInteractAcrossProfiles();
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isFalse();
}
@@ -528,7 +534,8 @@
public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() {
mockCrossProfileAppNotWhitelisted();
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isTrue();
}
@@ -541,7 +548,8 @@
Manifest.permission.INTERACT_ACROSS_PROFILES);
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isFalse();
}
@@ -550,7 +558,8 @@
when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(WORK_PROFILE_USER_ID))
.thenReturn(buildCrossProfileComponentName());
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isFalse();
}
@@ -562,7 +571,8 @@
when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(PERSONAL_PROFILE_USER_ID))
.thenReturn(buildCrossProfileComponentName());
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isFalse();
}
@@ -571,21 +581,23 @@
when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(OTHER_PROFILE_GROUP_USER_ID))
.thenReturn(buildCrossProfileComponentName());
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isTrue();
}
@Test
public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() {
assertThat(mCrossProfileAppsServiceImpl
- .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .canUserAttemptToConfigureInteractAcrossProfiles(
+ /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
.isTrue();
}
@Test
public void clearInteractAcrossProfilesAppOps() {
explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED);
- mCrossProfileAppsServiceImpl.clearInteractAcrossProfilesAppOps();
+ mCrossProfileAppsServiceImpl.clearInteractAcrossProfilesAppOps(/* userId= */ 0);
assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT);
}
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 47e7a37..d9467a5 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -23,7 +23,10 @@
java_test_host {
name: "PackageManagerServiceHostTests",
- srcs: ["src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
libs: [
"tradefed",
"junit",
@@ -34,11 +37,17 @@
"cts-host-utils",
"frameworks-base-hostutils",
"PackageManagerServiceHostTestsIntentVerifyUtils",
+ "block_device_writer_jar",
],
test_suites: ["general-tests"],
data: [
":PackageManagerTestApex",
":PackageManagerTestApexApp",
+ ":PackageManagerServiceServerTests",
+ ],
+ data_device_bins_both: [
+ "block_device_writer",
+ "fsverity_multilib",
],
java_resources: [
":PackageManagerTestOverlayActor",
diff --git a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
index f584599..2382548 100644
--- a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
@@ -27,6 +27,22 @@
value="pm uninstall com.android.cts.install.lib.testapp.A" />
</target_preparer>
+ <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="PackageManagerServiceServerTests.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <!-- The build system produces both 32 and 64 bit variants with bitness suffix. Let
+ FilePusher find the filename with bitness and push to a remote name without bitness.
+ -->
+ <option name="append-bitness" value="true" />
+ <option name="cleanup" value="true" />
+ <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
+ <option name="push" value="fsverity_multilib->/data/local/tmp/fsverity_multilib" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.HostTest">
<option name="jar" value="PackageManagerServiceHostTests.jar" />
</test>
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SettingsTest.java b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SettingsTest.java
new file mode 100644
index 0000000..a66906e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SettingsTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.blockdevicewriter.BlockDeviceWriter;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+ * atest com.android.server.pm.test.SettingsTest
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class SettingsTest extends BaseHostJUnit4Test {
+ private static final String DEVICE_TEST_PACKAGE_NAME =
+ "com.android.server.pm.test.service.server";
+ private static final String TEST_CLASS_NAME =
+ "com.android.server.pm.PackageManagerSettingsDeviceTests";
+
+ private static final String FSVERITY_EXECUTABLE = "/data/local/tmp/fsverity_multilib";
+ private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
+
+ @Test
+ public void testWriteCorruptHeaderBinaryXml() throws Exception {
+ // Corrupt 1st page, this will trigger error in initial resolve* call.
+ performTest("testWriteBinaryXmlSettings", 0);
+ }
+
+ @Test
+ public void testWriteCorruptHeaderTextXml() throws Exception {
+ // Corrupt 1st page, this will trigger error in initial resolve* call.
+ performTest("testWriteTextXmlSettings", 0);
+ }
+
+ @Test
+ public void testWriteCorruptDataBinaryXml() throws Exception {
+ // Corrupt 2nd page, this will trigger error in the XML parser.
+ performTest("testWriteBinaryXmlSettings", 1);
+ }
+
+ @Test
+ public void testWriteCorruptDataTextXml() throws Exception {
+ // Corrupt 2nd page, this will trigger error in the XML parser.
+ performTest("testWriteTextXmlSettings", 1);
+ }
+
+ private void performTest(String writeTest, int pageToCorrupt) throws Exception {
+ assertTrue(runDeviceTests(DEVICE_TEST_PACKAGE_NAME, TEST_CLASS_NAME,
+ writeTest));
+
+ int userId = getDevice().getCurrentUser();
+ File filesDir = new File(
+ "/data/user/" + userId + "/" + DEVICE_TEST_PACKAGE_NAME + "/files");
+ File packagesXml = new File(filesDir, "system/packages.xml");
+
+ // Make sure fs-verity is enabled.
+ enableFsVerity(packagesXml.getAbsolutePath());
+
+ // Damage the file directly against the block device.
+ BlockDeviceWriter.damageFileAgainstBlockDevice(getDevice(), packagesXml.getAbsolutePath(),
+ pageToCorrupt * 4096 + 1);
+ BlockDeviceWriter.dropCaches(getDevice());
+
+ assertTrue(runDeviceTests(DEVICE_TEST_PACKAGE_NAME, TEST_CLASS_NAME,
+ "testReadSettings"));
+ }
+
+ private void enableFsVerity(String path) throws Exception {
+ ITestDevice device = getDevice();
+ ArrayList<String> args = new ArrayList<>();
+ args.add(FSVERITY_EXECUTABLE);
+ args.add("enable");
+ args.add(path);
+
+ String cmd = String.join(" ", args);
+ CommandResult result = device.executeShellV2Command(cmd);
+ assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
+ result.getStatus());
+ }
+
+}
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index f7efcd1..e711cab 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -32,7 +32,6 @@
"services.people",
"services.usage",
"guava",
- "guava-android-testlib",
"androidx.test.core",
"androidx.test.ext.truth",
"androidx.test.runner",
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsDeviceTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsDeviceTests.java
new file mode 100644
index 0000000..e43f40d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsDeviceTests.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.PropertyInvalidatedCache;
+import android.os.Message;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.permission.persistence.RuntimePermissionsPersistence;
+import com.android.server.LocalServices;
+import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.UUID;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PackageManagerSettingsDeviceTests {
+ @Mock
+ RuntimePermissionsPersistence mRuntimePermissionsPersistence;
+ @Mock
+ LegacyPermissionDataProvider mPermissionDataProvider;
+ @Mock
+ DomainVerificationManagerInternal mDomainVerificationManager;
+ @Mock
+ Computer mComputer;
+
+ final TestHandler mHandler = new TestHandler((@NonNull Message msg) -> {
+ return true;
+ });
+
+ @Before
+ public void initializeMocks() {
+ MockitoAnnotations.initMocks(this);
+ when(mDomainVerificationManager.generateNewId())
+ .thenAnswer(invocation -> UUID.randomUUID());
+ }
+
+ @Before
+ public void setup() {
+ // Disable binder caches in this process.
+ PropertyInvalidatedCache.disableForTestMode();
+ }
+
+ @Before
+ public void createUserManagerServiceRef() throws ReflectiveOperationException {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
+ try {
+ // unregister the user manager from the local service
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ new UserManagerService(InstrumentationRegistry.getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Could not create user manager service; " + e);
+ }
+ });
+ }
+
+ // Write valid packages.xml, compare with the reserve copy.
+ @Test
+ public void testWriteBinaryXmlSettings() throws Exception {
+ // write out files and read
+ PackageManagerSettingsTests.writeOldFiles();
+ Settings settings = makeSettings();
+ assertTrue(settings.readLPw(mComputer, PackageManagerSettingsTests.createFakeUsers()));
+
+ // write out
+ settings.writeLPr(mComputer, /*sync=*/true);
+
+ File filesDir = InstrumentationRegistry.getContext().getFilesDir();
+ File packageXml = new File(filesDir, "system/packages.xml");
+ File packagesReserveCopyXml = new File(filesDir, "system/packages.xml.reservecopy");
+ // Primary.
+ assertTrue(packageXml.exists());
+ // For the test, we need a file with at least 2 pages.
+ assertThat(packageXml.length(), greaterThan(4096L));
+ // Reserve copy.
+ assertTrue(packagesReserveCopyXml.exists());
+ // Temporary backup.
+ assertFalse(new File(filesDir, "packages-backup.xml").exists());
+
+ // compare two copies, make sure they are the same
+ assertTrue(Arrays.equals(Files.readAllBytes(Path.of(packageXml.getAbsolutePath())),
+ Files.readAllBytes(Path.of(packagesReserveCopyXml.getAbsolutePath()))));
+ }
+
+ @Test
+ public void testWriteTextXmlSettings() throws Exception {
+ testWriteBinaryXmlSettings();
+
+ PackageManagerSettingsTests.writePackagesXml("system/packages.xml");
+
+ File filesDir = InstrumentationRegistry.getContext().getFilesDir();
+ File packageXml = new File(filesDir, "system/packages.xml");
+
+ assertTrue(packageXml.exists());
+ // For the test, we need a file with at least 2 pages.
+ assertThat(packageXml.length(), greaterThan(4096L));
+ }
+
+ // Read settings, verify.
+ @Test
+ public void testReadSettings() throws Exception {
+ // This test can be run separately. In this case packages.xml is missing.
+ assumeTrue(new File(InstrumentationRegistry.getContext().getFilesDir(),
+ "system/packages.xml").exists());
+ Settings settings = makeSettings();
+ assertTrue(settings.readLPw(mComputer, PackageManagerSettingsTests.createFakeUsers()));
+ PackageManagerSettingsTests.verifyKeySetMetaData(settings);
+ }
+
+ private Settings makeSettings() {
+ return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
+ mRuntimePermissionsPersistence, mPermissionDataProvider,
+ mDomainVerificationManager, mHandler,
+ new PackageManagerTracedLock());
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6a1ccc5..75484d1 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -56,7 +56,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
-import android.util.Log;
import android.util.LongSparseArray;
import androidx.test.InstrumentationRegistry;
@@ -108,7 +107,6 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageManagerSettingsTests {
- private static final String TAG = "PackageManagerSettingsTests";
private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final String PACKAGE_NAME_2 = "com.android.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
@@ -142,6 +140,25 @@
PropertyInvalidatedCache.disableForTestMode();
}
+ @Before
+ public void createUserManagerServiceRef() throws ReflectiveOperationException {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
+ try {
+ // unregister the user manager from the local service
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ new UserManagerService(InstrumentationRegistry.getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Could not create user manager service; " + e);
+ }
+ });
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ deleteFolder(InstrumentationRegistry.getContext().getFilesDir());
+ }
+
/** make sure our initialized KeySetManagerService metadata matches packages.xml */
@Test
public void testReadKeySetSettings() throws Exception {
@@ -1612,13 +1629,13 @@
UUID.randomUUID());
}
- private @NonNull List<UserInfo> createFakeUsers() {
+ static @NonNull List<UserInfo> createFakeUsers() {
ArrayList<UserInfo> users = new ArrayList<>();
users.add(new UserInfo(UserHandle.USER_SYSTEM, "test user", UserInfo.FLAG_INITIALIZED));
return users;
}
- private void writeFile(File file, byte[] data) {
+ private static void writeFile(File file, byte[] data) {
file.mkdirs();
try {
AtomicFile aFile = new AtomicFile(file);
@@ -1626,7 +1643,7 @@
fos.write(data);
aFile.finishWrite(fos);
} catch (IOException ioe) {
- Log.e(TAG, "Cannot write file " + file.getPath());
+ throw new RuntimeException("Cannot write file " + file.getPath(), ioe);
}
}
@@ -1640,7 +1657,7 @@
).getBytes());
}
- private void writePackagesXml(String fileName) {
+ static void writePackagesXml(String fileName) {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), fileName),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<packages>"
@@ -1748,7 +1765,7 @@
.getBytes());
}
- private void writeStoppedPackagesXml() {
+ private static void writeStoppedPackagesXml() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"),
( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<stopped-packages>"
@@ -1758,7 +1775,7 @@
.getBytes());
}
- private void writePackagesList() {
+ private static void writePackagesList() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages.list"),
( "com.android.app1 11000 0 /data/data/com.android.app1 seinfo1"
+ "com.android.app2 11001 0 /data/data/com.android.app2 seinfo2"
@@ -1766,7 +1783,7 @@
.getBytes());
}
- private void deleteSystemFolder() {
+ private static void deleteSystemFolder() {
File systemFolder = new File(InstrumentationRegistry.getContext().getFilesDir(), "system");
deleteFolder(systemFolder);
}
@@ -1781,7 +1798,7 @@
folder.delete();
}
- private void writeOldFiles() {
+ static void writeOldFiles() {
deleteSystemFolder();
writePackagesXml("system/packages.xml");
writeStoppedPackagesXml();
@@ -1795,25 +1812,6 @@
writePackagesList();
}
- @Before
- public void createUserManagerServiceRef() throws ReflectiveOperationException {
- InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
- try {
- // unregister the user manager from the local service
- LocalServices.removeServiceForTest(UserManagerInternal.class);
- new UserManagerService(InstrumentationRegistry.getContext());
- } catch (Exception e) {
- e.printStackTrace();
- fail("Could not create user manager service; " + e);
- }
- });
- }
-
- @After
- public void tearDown() throws Exception {
- deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
- }
-
private Settings makeSettings() {
return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider,
@@ -1821,7 +1819,7 @@
new PackageManagerTracedLock());
}
- private void verifyKeySetMetaData(Settings settings)
+ static void verifyKeySetMetaData(Settings settings)
throws ReflectiveOperationException, IllegalAccessException {
WatchedArrayMap<String, PackageSetting> packages = settings.mPackages;
KeySetManagerService ksms = settings.getKeySetManagerService();
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index f915298..101498a 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -92,7 +92,10 @@
certificate: "platform",
platform_apis: true,
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
optimize: {
enabled: false,
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 33ac735..ea0481e 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -43,6 +43,9 @@
<!-- needed by TrustManagerServiceTest to access LockSettings' secure storage -->
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <!-- needed by GameManagerServiceTest because GameManager creates a UidObserver -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
<application android:testOnly="true"
android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/mockingservicestests/src/android/hardware/face/FaceManagerTest.java b/services/tests/mockingservicestests/src/android/hardware/face/FaceManagerTest.java
new file mode 100644
index 0000000..f3feb02
--- /dev/null
+++ b/services/tests/mockingservicestests/src/android/hardware/face/FaceManagerTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FaceManagerTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ Context mContext;
+ @Mock
+ IFaceService mService;
+
+ @Captor
+ ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor;
+
+ List<FaceSensorPropertiesInternal> mProps;
+ FaceManager mFaceManager;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ mFaceManager = new FaceManager(mContext, mService);
+ mProps = new ArrayList<>();
+ mProps.add(new FaceSensorPropertiesInternal(
+ 0 /* id */,
+ FaceSensorProperties.STRENGTH_STRONG,
+ 1 /* maxTemplatesAllowed */,
+ new ArrayList<>() /* conponentInfo */,
+ FaceSensorProperties.TYPE_UNKNOWN,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */));
+ }
+
+ @Test
+ public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException {
+ verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
+
+ mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
+ List<FaceSensorPropertiesInternal> actual = mFaceManager.getSensorPropertiesInternal();
+
+ assertThat(actual).isEqualTo(mProps);
+ verify(mService, never()).getSensorPropertiesInternal(any());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/android/hardware/face/OWNERS b/services/tests/mockingservicestests/src/android/hardware/face/OWNERS
new file mode 100644
index 0000000..6a2192a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/android/hardware/face/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/biometrics/OWNERS
diff --git a/services/tests/mockingservicestests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/services/tests/mockingservicestests/src/android/hardware/fingerprint/FingerprintManagerTest.java
new file mode 100644
index 0000000..558202d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/android/hardware/fingerprint/FingerprintManagerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@RunWith(MockitoJUnitRunner.class)
+public class FingerprintManagerTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ Context mContext;
+ @Mock
+ IFingerprintService mService;
+
+ @Captor
+ ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mCaptor;
+
+ List<FingerprintSensorPropertiesInternal> mProps;
+ FingerprintManager mFingerprintManager;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ mFingerprintManager = new FingerprintManager(mContext, mService);
+ mProps = new ArrayList<>();
+ mProps.add(new FingerprintSensorPropertiesInternal(
+ 0 /* sensorId */,
+ FingerprintSensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ new ArrayList<>() /* componentInfo */,
+ FingerprintSensorProperties.TYPE_UNKNOWN,
+ true /* halControlsIllumination */,
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ new ArrayList<>() /* sensorLocations */));
+ }
+
+ @Test
+ public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException {
+ verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
+
+ mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
+ List<FingerprintSensorPropertiesInternal> actual =
+ mFingerprintManager.getSensorPropertiesInternal();
+
+ assertThat(actual).isEqualTo(mProps);
+ verify(mService, never()).getSensorPropertiesInternal(any());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/android/hardware/fingerprint/OWNERS b/services/tests/mockingservicestests/src/android/hardware/fingerprint/OWNERS
new file mode 100644
index 0000000..3edcf70
--- /dev/null
+++ b/services/tests/mockingservicestests/src/android/hardware/fingerprint/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/biometrics/OWNERS
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index e2b25ef..ef470fe 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -128,6 +128,7 @@
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.app.role.RoleManager;
+import android.app.tare.EconomyManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
@@ -207,6 +208,7 @@
private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
private static final int SYSTEM_UI_UID = 12345;
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
+ private static final int TEST_CALLING_UID_2 = TEST_CALLING_UID + 1;
private long mAppStandbyWindow;
private long mAllowWhileIdleWindow;
@@ -508,7 +510,7 @@
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
verify(mBatteryManager).isCharging();
- setTareEnabled(false);
+ setTareEnabled(EconomyManager.ENABLED_MODE_OFF);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
@@ -658,10 +660,10 @@
mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
}
- private void setTareEnabled(boolean enabled) {
- when(mEconomyManagerInternal.isEnabled(eq(AlarmManagerEconomicPolicy.POLICY_ALARM)))
- .thenReturn(enabled);
- mService.mConstants.onTareEnabledStateChanged(enabled);
+ private void setTareEnabled(int enabledMode) {
+ when(mEconomyManagerInternal.getEnabledMode(eq(AlarmManagerEconomicPolicy.POLICY_ALARM)))
+ .thenReturn(enabledMode);
+ mService.mConstants.onTareEnabledModeChanged(enabledMode);
}
/**
@@ -2091,7 +2093,7 @@
@Test
public void tareThrottling() {
- setTareEnabled(true);
+ setTareEnabled(EconomyManager.ENABLED_MODE_ON);
final ArgumentCaptor<EconomyManagerInternal.AffordabilityChangeListener> listenerCaptor =
ArgumentCaptor.forClass(EconomyManagerInternal.AffordabilityChangeListener.class);
final ArgumentCaptor<EconomyManagerInternal.ActionBill> billCaptor =
@@ -3411,16 +3413,55 @@
final int type = ((i & 1) == 0) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
setTestAlarm(type, mNowElapsedTest + i, getNewMockPendingIntent());
}
+ for (int i = 0; i < 4; i++) {
+ final int type = ((i & 1) == 0) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
+ setTestAlarm(
+ type,
+ mNowElapsedTest + i,
+ getNewMockPendingIntent(),
+ 0,
+ FLAG_STANDALONE,
+ TEST_CALLING_UID_2);
+ }
mNowElapsedTest += 100;
mTestTimer.expire();
- verify(() -> MetricsHelper.pushAlarmBatchDelivered(10, 5));
+ final ArgumentCaptor<int[]> uidsCaptor = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> alarmsPerUidCaptor = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> wakeupAlarmsPerUidCaptor = ArgumentCaptor.forClass(int[].class);
+
+ verify(() -> MetricsHelper.pushAlarmBatchDelivered(
+ eq(14),
+ eq(7),
+ uidsCaptor.capture(),
+ alarmsPerUidCaptor.capture(),
+ wakeupAlarmsPerUidCaptor.capture()));
+ assertEquals(2, uidsCaptor.getValue().length);
+ assertEquals(2, alarmsPerUidCaptor.getValue().length);
+ assertEquals(2, wakeupAlarmsPerUidCaptor.getValue().length);
+ final int uid1Idx = uidsCaptor.getValue()[0] == TEST_CALLING_UID ? 0 : 1;
+ final int uid2Idx = 1 - uid1Idx;
+ assertEquals(TEST_CALLING_UID, uidsCaptor.getValue()[uid1Idx]);
+ assertEquals(TEST_CALLING_UID_2, uidsCaptor.getValue()[uid2Idx]);
+ assertEquals(10, alarmsPerUidCaptor.getValue()[uid1Idx]);
+ assertEquals(5, wakeupAlarmsPerUidCaptor.getValue()[uid1Idx]);
+ assertEquals(4, alarmsPerUidCaptor.getValue()[uid2Idx]);
+ assertEquals(2, wakeupAlarmsPerUidCaptor.getValue()[uid2Idx]);
}
@Test
- public void tareEventPushed() throws Exception {
- setTareEnabled(true);
+ public void tareEventPushed_on() throws Exception {
+ setTareEnabled(EconomyManager.ENABLED_MODE_ON);
+ runTareEventPushed();
+ }
+ @Test
+ public void tareEventPushed_shadow() throws Exception {
+ setTareEnabled(EconomyManager.ENABLED_MODE_SHADOW);
+ runTareEventPushed();
+ }
+
+ private void runTareEventPushed() throws Exception {
for (int i = 0; i < 10; i++) {
final int type = (i % 2 == 1) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
setTestAlarm(type, mNowElapsedTest + i, getNewMockPendingIntent());
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 45fe795..41ae50b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -1876,6 +1876,54 @@
}
@Test
+ public void testReplacePending_withUrgentBroadcast() throws Exception {
+ // The behavior is same with the legacy queue but AMS takes care of finding
+ // the right queue and replacing the broadcast.
+ Assume.assumeTrue(mImpl == Impl.MODERN);
+
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ final Intent timeTickFirst = new Intent(Intent.ACTION_TIME_TICK);
+ timeTickFirst.putExtra(Intent.EXTRA_INDEX, "one");
+ timeTickFirst.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ timeTickFirst.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+ final Intent timeTickSecond = new Intent(Intent.ACTION_TIME_TICK);
+ timeTickFirst.putExtra(Intent.EXTRA_INDEX, "second");
+ timeTickSecond.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ final Intent timeTickThird = new Intent(Intent.ACTION_TIME_TICK);
+ timeTickFirst.putExtra(Intent.EXTRA_INDEX, "third");
+ timeTickThird.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ try (SyncBarrier b = new SyncBarrier()) {
+ enqueueBroadcast(makeBroadcastRecord(timeTickFirst, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+ enqueueBroadcast(makeBroadcastRecord(timeTickSecond, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+ enqueueBroadcast(makeBroadcastRecord(timeTickThird, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+ }
+
+ waitForIdle();
+ final IApplicationThread blueThread = mAms.getProcessRecordLocked(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE)).getThread();
+ final InOrder inOrder = inOrder(blueThread);
+
+ // First broadcast is delivered.
+ timeTickFirst.setClassName(PACKAGE_BLUE, CLASS_BLUE);
+ inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
+ filterAndExtrasEquals(timeTickFirst),
+ null, null, null, null, null, false, false, null, null));
+
+ // Second broadcast should be replaced by third broadcast.
+ timeTickThird.setClassName(PACKAGE_BLUE, CLASS_BLUE);
+ inOrder.verify(blueThread).scheduleReceiverList(manifestReceiver(
+ filterAndExtrasEquals(timeTickThird),
+ null, null, null, null, null, false, false, null, null));
+ }
+
+ @Test
public void testIdleAndBarrier() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index c4c50424..32243f0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -36,6 +36,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -45,6 +46,7 @@
import android.Manifest;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameModeConfiguration;
import android.app.GameModeInfo;
@@ -2212,4 +2214,106 @@
assertEquals(expectedInterventionListOutput,
gameManagerService.getInterventionList(mPackageName, USER_ID_1));
}
+
+ @Test
+ public void testGamePowerMode_gamePackage() throws Exception {
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ String[] packages = {mPackageName};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+ }
+
+ @Test
+ public void testGamePowerMode_twoGames() throws Exception {
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ String[] packages1 = {mPackageName};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
+ String someGamePkg = "some.game";
+ String[] packages2 = {someGamePkg};
+ int somePackageId = DEFAULT_PACKAGE_UID + 1;
+ when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+ HashMap<Integer, Boolean> powerState = new HashMap<>();
+ doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
+ .when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ assertTrue(powerState.get(Mode.GAME));
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ assertTrue(powerState.get(Mode.GAME));
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ assertFalse(powerState.get(Mode.GAME));
+ }
+
+ @Test
+ public void testGamePowerMode_twoGamesOverlap() throws Exception {
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ String[] packages1 = {mPackageName};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
+ String someGamePkg = "some.game";
+ String[] packages2 = {someGamePkg};
+ int somePackageId = DEFAULT_PACKAGE_UID + 1;
+ when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+ }
+
+ @Test
+ public void testGamePowerMode_released() throws Exception {
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ String[] packages = {mPackageName};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+ }
+
+ @Test
+ public void testGamePowerMode_noPackage() throws Exception {
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ String[] packages = {};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+ }
+
+ @Test
+ public void testGamePowerMode_notAGamePackage() throws Exception {
+ mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ String[] packages = {"someapp"};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+ }
+
+ @Test
+ public void testGamePowerMode_notAGamePackageNotReleased() throws Exception {
+ mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ String[] packages = {"someapp"};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, false);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
index 98e895a..cd5ac7bc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -35,6 +35,8 @@
import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -754,6 +756,32 @@
verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
}
+ @Test
+ public void testIsUidInForegroundForBackgroundState() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+ assertFalse(mIntf.isUidInForeground(UID));
+
+ procStateBuilder(UID)
+ .nonExistentState()
+ .update();
+ assertFalse(mIntf.isUidInForeground(UID));
+ }
+
+ @Test
+ public void testIsUidInForegroundForForegroundState() {
+ procStateBuilder(UID)
+ .topState()
+ .update();
+ assertTrue(mIntf.isUidInForeground(UID));
+
+ procStateBuilder(UID)
+ .foregroundServiceState()
+ .update();
+ assertTrue(mIntf.isUidInForeground(UID));
+ }
+
public void testUidStateChangedCallback(int initialState, int finalState) {
int initialUidState = processStateToUidState(initialState);
int finalUidState = processStateToUidState(finalState);
@@ -806,7 +834,7 @@
private AppOpsUidStateTracker mIntf;
private int mUid;
private int mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
- private int mCapability = 0;
+ private int mCapability = ActivityManager.PROCESS_CAPABILITY_NONE;
private UidProcStateUpdateBuilder(AppOpsUidStateTracker intf, int uid) {
mUid = uid;
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
index f535997..201da35 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
@@ -46,8 +46,9 @@
@Test
public void getBackupTransportFutureTimeoutMillis_set_returnsSetValue() {
- DeviceConfig.setProperty("backup_and_restore", "backup_transport_future_timeout_millis",
- "1234", false);
+ DeviceConfig.setProperty(/*namespace=*/ "backup_and_restore",
+ /*name=*/ "backup_transport_future_timeout_millis",
+ /*value=*/ "1234", /*makeDefault=*/ false);
assertThat(
BackupAndRestoreFeatureFlags.getBackupTransportFutureTimeoutMillis()).isEqualTo(
@@ -63,11 +64,44 @@
@Test
public void getBackupTransportCallbackTimeoutMillis_set_returnsSetValue() {
- DeviceConfig.setProperty("backup_and_restore", "backup_transport_callback_timeout_millis",
- "5678", false);
+ DeviceConfig.setProperty(/*namespace=*/ "backup_and_restore",
+ /*name=*/ "backup_transport_callback_timeout_millis",
+ /*value=*/ "5678", /*makeDefault=*/ false);
assertThat(
BackupAndRestoreFeatureFlags.getBackupTransportCallbackTimeoutMillis()).isEqualTo(
5678);
}
+
+ @Test
+ public void getFullBackupWriteToTransportBufferSizeBytes_notSet_returnsDefault() {
+ assertThat(BackupAndRestoreFeatureFlags.getFullBackupWriteToTransportBufferSizeBytes())
+ .isEqualTo(8 * 1024);
+ }
+
+ @Test
+ public void getFullBackupWriteToTransportBufferSizeBytes_set_returnsSetValue() {
+ DeviceConfig.setProperty(/*namespace=*/ "backup_and_restore",
+ /*name=*/ "full_backup_write_to_transport_buffer_size_bytes",
+ /*value=*/ "5678", /*makeDefault=*/ false);
+
+ assertThat(BackupAndRestoreFeatureFlags.getFullBackupWriteToTransportBufferSizeBytes())
+ .isEqualTo(5678);
+ }
+
+ @Test
+ public void getFullBackupUtilsRouteBufferSizeBytes_notSet_returnsDefault() {
+ assertThat(BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes())
+ .isEqualTo(32 * 1024);
+ }
+
+ @Test
+ public void getFullBackupUtilsRouteBufferSizeBytes_set_returnsSetValue() {
+ DeviceConfig.setProperty(/*namespace=*/ "backup_and_restore",
+ /*name=*/ "full_backup_utils_route_buffer_size_bytes",
+ /*value=*/ "5678", /*makeDefault=*/ false);
+
+ assertThat(BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes())
+ .isEqualTo(5678);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index f2cba40..3d36c1c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -18,6 +18,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
@@ -80,6 +82,8 @@
@Mock
private DisplayBlanker mDisplayBlankerMock;
@Mock
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
+ @Mock
private LogicalDisplay mLogicalDisplayMock;
@Mock
private DisplayDevice mDisplayDeviceMock;
@@ -169,7 +173,7 @@
mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
- });
+ }, mHighBrightnessModeMetadataMock);
when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
@@ -251,4 +255,35 @@
});
when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
}
+
+ @Test
+ public void testDisplayBrightnessFollowers() {
+ setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+ DisplayPowerController2 defaultDpc = new DisplayPowerController2(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ }, mHighBrightnessModeMetadataMock);
+ DisplayPowerController2 followerDpc = new DisplayPowerController2(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ }, mHighBrightnessModeMetadataMock);
+
+ defaultDpc.addDisplayBrightnessFollower(followerDpc);
+
+ defaultDpc.setBrightness(0.3f);
+ assertEquals(defaultDpc.getBrightnessInfo().brightness,
+ followerDpc.getBrightnessInfo().brightness, 0);
+
+ defaultDpc.setBrightness(0.6f);
+ assertEquals(defaultDpc.getBrightnessInfo().brightness,
+ followerDpc.getBrightnessInfo().brightness, 0);
+
+ float brightness = 0.1f;
+ defaultDpc.clearDisplayBrightnessFollowers();
+ defaultDpc.setBrightness(brightness);
+ assertNotEquals(brightness, followerDpc.getBrightnessInfo().brightness, 0);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 4f8cb88..b6388cc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -19,6 +19,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
@@ -84,6 +86,8 @@
@Mock
private DisplayDevice mDisplayDeviceMock;
@Mock
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
+ @Mock
private BrightnessTracker mBrightnessTrackerMock;
@Mock
private BrightnessSetting mBrightnessSettingMock;
@@ -151,7 +155,7 @@
mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
- });
+ }, mHighBrightnessModeMetadataMock);
when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
@@ -233,4 +237,35 @@
});
when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
}
+
+ @Test
+ public void testDisplayBrightnessFollowers() {
+ setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+ DisplayPowerController defaultDpc = new DisplayPowerController(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ }, mHighBrightnessModeMetadataMock);
+ DisplayPowerController followerDpc = new DisplayPowerController(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ }, mHighBrightnessModeMetadataMock);
+
+ defaultDpc.addDisplayBrightnessFollower(followerDpc);
+
+ defaultDpc.setBrightness(0.3f);
+ assertEquals(defaultDpc.getBrightnessInfo().brightness,
+ followerDpc.getBrightnessInfo().brightness, 0);
+
+ defaultDpc.setBrightness(0.6f);
+ assertEquals(defaultDpc.getBrightnessInfo().brightness,
+ followerDpc.getBrightnessInfo().brightness, 0);
+
+ float brightness = 0.1f;
+ defaultDpc.clearDisplayBrightnessFollowers();
+ defaultDpc.setBrightness(brightness);
+ assertNotEquals(brightness, followerDpc.getBrightnessInfo().brightness, 0);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index a8b8f91..7281fafc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -30,6 +30,7 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -370,11 +371,12 @@
}
/**
- * Confirm that {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int)}
+ * Confirm that
+ * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)}
* returns a job with the correct delay and deadline constraints.
*/
@Test
- public void testGetRescheduleJobForFailure() {
+ public void testGetRescheduleJobForFailure_timingCalculations() {
final long nowElapsed = sElapsedRealtimeClock.millis();
final long initialBackoffMs = MINUTE_IN_MILLIS;
mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3;
@@ -387,15 +389,18 @@
// failure = 0, systemStop = 1
JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
// failure = 0, systemStop = 2
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
JobParameters.INTERNAL_STOP_REASON_PREEMPT);
// failure = 0, systemStop = 3
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_CONSTRAINT_CHARGING,
JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED);
assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
@@ -403,6 +408,7 @@
// failure = 0, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) {
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_SYSTEM_PROCESSING,
JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED);
}
assertEquals(nowElapsed + 2 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
@@ -410,18 +416,52 @@
// failure = 1, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_TIMEOUT,
JobParameters.INTERNAL_STOP_REASON_TIMEOUT);
assertEquals(nowElapsed + 3 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
// failure = 2, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
assertEquals(nowElapsed + 4 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
}
/**
+ * Confirm that
+ * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)}
+ * returns a job that is correctly marked as demoted by the user.
+ */
+ @Test
+ public void testGetRescheduleJobForFailure_userDemotion() {
+ JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo());
+ assertEquals(0, originalJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
+
+ // Reschedule for a non-user reason
+ JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob,
+ JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
+ assertEquals(0,
+ rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
+
+ // Reschedule for a user reason
+ rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_USER,
+ JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP);
+ assertNotEquals(0,
+ rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
+
+ // Reschedule a previously demoted job for a non-user reason
+ rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+ JobParameters.STOP_REASON_CONSTRAINT_CHARGING,
+ JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED);
+ assertNotEquals(0,
+ rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
+ }
+
+ /**
* Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
* with the correct delay and deadline constraints if the periodic job is scheduled with the
* minimum possible period.
@@ -731,6 +771,7 @@
JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob",
createJobInfo().setPeriodic(HOUR_IN_MILLIS));
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
@@ -739,6 +780,7 @@
advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes
failedJob = mService.getRescheduleJobForFailureLocked(job,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes
@@ -748,6 +790,7 @@
advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes
failedJob = mService.getRescheduleJobForFailureLocked(job,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes
@@ -759,6 +802,7 @@
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes
failedJob = mService.getRescheduleJobForFailureLocked(job,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes
@@ -856,6 +900,7 @@
JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob",
createJobInfo().setPeriodic(HOUR_IN_MILLIS));
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
long now = sElapsedRealtimeClock.millis();
long nextWindowStartTime = now + HOUR_IN_MILLIS;
@@ -893,6 +938,7 @@
"testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob",
createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
// First window starts 30 minutes from now.
advanceElapsedClock(30 * MINUTE_IN_MILLIS);
@@ -935,6 +981,7 @@
"testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod",
createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS));
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+ JobParameters.STOP_REASON_UNDEFINED,
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
// First window starts 6.625 days from now.
advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index d2ee9ff..2c47fd9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -19,6 +19,7 @@
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.spyOn;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
@@ -132,6 +133,98 @@
}
@Test
+ public void testCanRunInBatterySaver_regular() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar")).build();
+ JobStatus job = createJobStatus(jobInfo);
+ assertFalse(job.canRunInBatterySaver());
+ job.disallowRunInBatterySaverAndDoze();
+ assertFalse(job.canRunInBatterySaver());
+ }
+
+ @Test
+ public void testCanRunInBatterySaver_expedited() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setExpedited(true)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+ markExpeditedQuotaApproved(job, true);
+ assertTrue(job.canRunInBatterySaver());
+ job.disallowRunInBatterySaverAndDoze();
+ assertFalse(job.canRunInBatterySaver());
+
+ // Reset the job
+ job = createJobStatus(jobInfo);
+ markExpeditedQuotaApproved(job, true);
+ spyOn(job);
+ when(job.shouldTreatAsExpeditedJob()).thenReturn(false);
+ job.startedAsExpeditedJob = true;
+ assertTrue(job.canRunInBatterySaver());
+ job.disallowRunInBatterySaverAndDoze();
+ assertFalse(job.canRunInBatterySaver());
+ }
+
+ @Test
+ public void testCanRunInBatterySaver_userInitiated() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setUserInitiated(true)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+ assertTrue(job.canRunInBatterySaver());
+ // User-initiated privilege should trump bs & doze requirement.
+ job.disallowRunInBatterySaverAndDoze();
+ assertTrue(job.canRunInBatterySaver());
+ }
+
+ @Test
+ public void testCanRunInDoze_regular() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar")).build();
+ JobStatus job = createJobStatus(jobInfo);
+ assertFalse(job.canRunInDoze());
+ job.disallowRunInBatterySaverAndDoze();
+ assertFalse(job.canRunInDoze());
+ }
+
+ @Test
+ public void testCanRunInDoze_expedited() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setExpedited(true)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+ markExpeditedQuotaApproved(job, true);
+ assertTrue(job.canRunInDoze());
+ job.disallowRunInBatterySaverAndDoze();
+ assertFalse(job.canRunInDoze());
+
+ // Reset the job
+ job = createJobStatus(jobInfo);
+ markExpeditedQuotaApproved(job, true);
+ spyOn(job);
+ when(job.shouldTreatAsExpeditedJob()).thenReturn(false);
+ job.startedAsExpeditedJob = true;
+ assertTrue(job.canRunInDoze());
+ job.disallowRunInBatterySaverAndDoze();
+ assertFalse(job.canRunInDoze());
+ }
+
+ @Test
+ public void testCanRunInDoze_userInitiated() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setUserInitiated(true)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+ assertTrue(job.canRunInDoze());
+ // User-initiated privilege should trump bs & doze requirement.
+ job.disallowRunInBatterySaverAndDoze();
+ assertTrue(job.canRunInDoze());
+ }
+
+ @Test
public void testMediaBackupExemption_lateConstraint() {
final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
.addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
@@ -376,6 +469,34 @@
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
}
+ @Test
+ public void testShouldTreatAsUserInitiated() {
+ JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setUserInitiated(false)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+
+ assertFalse(job.shouldTreatAsUserInitiatedJob());
+
+ jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setUserInitiated(true)
+ .build();
+ job = createJobStatus(jobInfo);
+
+ assertTrue(job.shouldTreatAsUserInitiatedJob());
+
+ JobStatus rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME,
+ 0, 0, 0, 0);
+ assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob());
+
+ job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
+ assertFalse(job.shouldTreatAsUserInitiatedJob());
+
+ rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME,
+ 0, 0, 0, 0);
+ assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob());
+ }
+
/**
* Test {@link JobStatus#wouldBeReadyWithConstraint} on explicit constraints that weren't
* requested.
@@ -907,6 +1028,13 @@
assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false));
}
+ private void markExpeditedQuotaApproved(JobStatus job, boolean isApproved) {
+ if (job.isRequestedExpeditedJob()) {
+ job.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), isApproved);
+ job.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), isApproved);
+ }
+ }
+
private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) {
job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index c46ebf2..71280ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
+import android.app.tare.EconomyManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -89,7 +90,7 @@
.mockStatic(LocalServices.class)
.startMocking();
when(mIrs.getLock()).thenReturn(new Object());
- when(mIrs.isEnabled()).thenReturn(true);
+ when(mIrs.getEnabledMode()).thenReturn(EconomyManager.ENABLED_MODE_ON);
when(mIrs.getInstalledPackages()).thenReturn(mInstalledPackages);
when(mAnalyst.getReports()).thenReturn(mReports);
mTestFileDir = new File(getContext().getFilesDir(), "scribe_test");
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ced2a4b..3a7b9a4 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -62,6 +62,7 @@
"junit-params",
"ActivityContext",
"coretests-aidl",
+ "securebox",
],
libs: [
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 19857ed..26d681b 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -37,7 +37,10 @@
crossProfileIntentFilterAccessControl='20'
crossProfileIntentResolutionStrategy='0'
mediaSharedWithParent='true'
- credentialSharableWithParent='false'
+ credentialShareableWithParent='false'
+ showInSettings='23'
+ inheritDevicePolicy='450'
+ deleteAppWithParent='false'
/>
</profile-type>
<profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
index 653ed1a..245db46 100644
--- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
@@ -16,40 +16,104 @@
package com.android.server;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.job.JobScheduler;
import android.content.Context;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorProperties;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.util.FrameworkStatsLog;
+
+import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class BinaryTransparencyServiceTest {
+ private static final String TAG = "BinaryTransparencyServiceTest";
+
private Context mContext;
private BinaryTransparencyService mBinaryTransparencyService;
private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface;
+ private DeviceConfig.Properties mOriginalBiometricsFlags;
+
+ @Mock
+ private BinaryTransparencyService.BiometricLogger mBiometricLogger;
+ @Mock
+ private FingerprintManager mFpManager;
+ @Mock
+ private FaceManager mFaceManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+
+ @Captor
+ private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback>
+ mFpAuthenticatorsRegisteredCaptor;
+ @Captor
+ private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback>
+ mFaceAuthenticatorsRegisteredCaptor;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
mContext = spy(ApplicationProvider.getApplicationContext());
- mBinaryTransparencyService = new BinaryTransparencyService(mContext);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+
+ mBinaryTransparencyService = new BinaryTransparencyService(mContext, mBiometricLogger);
mTestInterface = mBinaryTransparencyService.new BinaryTransparencyServiceImpl();
+ mOriginalBiometricsFlags = DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BIOMETRICS);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ try {
+ DeviceConfig.setProperties(mOriginalBiometricsFlags);
+ } catch (DeviceConfig.BadConfigException e) {
+ Log.e(TAG, "Failed to reset biometrics flags to the original values before test. "
+ + e);
+ }
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
private void prepSignedInfo() {
@@ -66,6 +130,14 @@
args, null, new ResultReceiver(null));
}
+ private void prepBiometricsTesting() {
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mContext.getSystemService(FingerprintManager.class)).thenReturn(mFpManager);
+ when(mContext.getSystemService(FaceManager.class)).thenReturn(mFaceManager);
+ }
+
@Test
public void getSignedImageInfo_preInitialize_returnsUninitializedString() {
String result = mTestInterface.getSignedImageInfo();
@@ -98,7 +170,10 @@
prepApexInfo();
List result = mTestInterface.getApexInfo();
Assert.assertNotNull("Apex info map should not be null", result);
- Assert.assertFalse("Apex info map should not be empty", result.isEmpty());
+ // TODO(265244016): When PackageManagerInternal is a mock, it's harder to keep the
+ // `measurePackage` working in unit test. Disable it for now. We may need more refactoring
+ // or cover this in integration tests.
+ // Assert.assertFalse("Apex info map should not be empty", result.isEmpty());
}
@Test
@@ -111,13 +186,111 @@
Assert.assertNotNull(pm);
List<Bundle> castedResult = (List<Bundle>) resultList;
for (Bundle resultBundle : castedResult) {
- PackageInfo resultPackageInfo = resultBundle.getParcelable(
- BinaryTransparencyService.BUNDLE_PACKAGE_INFO, PackageInfo.class);
- Assert.assertNotNull("PackageInfo for APEX should not be null",
- resultPackageInfo);
- Assert.assertTrue(resultPackageInfo.packageName + "is not an APEX!",
- resultPackageInfo.isApex);
+ String packageName = resultBundle.getString(
+ BinaryTransparencyService.BUNDLE_PACKAGE_NAME);
+ Assert.assertNotNull("Package name for APEX should not be null", packageName);
+ Assert.assertTrue(packageName + "is not an APEX!",
+ resultBundle.getBoolean(
+ BinaryTransparencyService.BUNDLE_PACKAGE_IS_APEX));
}
}
+ @Test
+ public void testCollectBiometricProperties_disablesFeature() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+ BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+ Boolean.FALSE.toString(),
+ false /* makeDefault */);
+
+ mBinaryTransparencyService.collectBiometricProperties();
+
+ verify(mBiometricLogger, never()).logStats(anyInt(), anyInt(), anyInt(), anyInt(),
+ anyString(), anyString(), anyString(), anyString(), anyString());
+ }
+
+ @Test
+ public void testCollectBiometricProperties_enablesFeature_logsFingerprintProperties()
+ throws RemoteException {
+ prepBiometricsTesting();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+ BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+ Boolean.TRUE.toString(),
+ false /* makeDefault */);
+ final List<FingerprintSensorPropertiesInternal> props = List.of(
+ new FingerprintSensorPropertiesInternal(
+ 1 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ List.of(new ComponentInfoInternal("sensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */,
+ "1.01" /* firmwareVersion */, "00000001" /* serialNumber */,
+ "" /* softwareVersion */)),
+ FingerprintSensorProperties.TYPE_REAR,
+ true /* resetLockoutRequiresHardwareAuthToken */));
+
+ mBinaryTransparencyService.collectBiometricProperties();
+
+ verify(mFpManager).addAuthenticatorsRegisteredCallback(mFpAuthenticatorsRegisteredCaptor
+ .capture());
+ mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
+
+ verify(mBiometricLogger, times(1)).logStats(
+ eq(1) /* sensorId */,
+ eq(FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FINGERPRINT),
+ eq(FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_REAR),
+ eq(FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_STRONG),
+ eq("sensor") /* componentId */,
+ eq("vendor/model/revision") /* hardwareVersion */,
+ eq("1.01") /* firmwareVersion */,
+ eq("00000001") /* serialNumber */,
+ eq("") /* softwareVersion */
+ );
+ }
+
+ @Test
+ public void testCollectBiometricProperties_enablesFeature_logsFaceProperties()
+ throws RemoteException {
+ prepBiometricsTesting();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+ BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+ Boolean.TRUE.toString(),
+ false /* makeDefault */);
+ final List<FaceSensorPropertiesInternal> props = List.of(
+ new FaceSensorPropertiesInternal(
+ 1 /* sensorId */,
+ SensorProperties.STRENGTH_CONVENIENCE,
+ 1 /* maxEnrollmentsPerUser */,
+ List.of(new ComponentInfoInternal("sensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */,
+ "1.01" /* firmwareVersion */, "00000001" /* serialNumber */,
+ "" /* softwareVersion */)),
+ FaceSensorProperties.TYPE_RGB,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ true /* resetLockoutRequiresHardwareAuthToken */));
+
+ mBinaryTransparencyService.collectBiometricProperties();
+
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(mFaceAuthenticatorsRegisteredCaptor
+ .capture());
+ mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
+
+ verify(mBiometricLogger, times(1)).logStats(
+ eq(1) /* sensorId */,
+ eq(FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FACE),
+ eq(FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FACE_RGB),
+ eq(FrameworkStatsLog
+ .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_CONVENIENCE),
+ eq("sensor") /* componentId */,
+ eq("vendor/model/revision") /* hardwareVersion */,
+ eq("1.01") /* firmwareVersion */,
+ eq("00000001") /* serialNumber */,
+ eq("") /* softwareVersion */
+ );
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 448ffe5..4d1d2b2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -16,13 +16,19 @@
package com.android.server.accessibility;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_MENU_IN_SYSTEM;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.server.accessibility.AccessibilityManagerService.MENU_SERVICE_RELATIVE_CLASS_NAME;
import static com.google.common.truth.Truth.assertThat;
@@ -93,6 +99,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.List;
/**
* APCT tests for {@link AccessibilityManagerService}.
@@ -182,6 +189,7 @@
mA11yms = new AccessibilityManagerService(
mTestableContext,
+ mHandler,
mMockPackageManager,
mMockSecurityPolicy,
mMockSystemActionPerformer,
@@ -364,6 +372,7 @@
);
mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, true);
+ mHandler.sendAllMessages();
ArgumentCaptor<Display> displayCaptor = ArgumentCaptor.forClass(Display.class);
verify(mInputFilter, timeout(100)).refreshMagnificationMode(displayCaptor.capture());
@@ -514,6 +523,113 @@
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
}
+ @Test
+ public void testMigrateA11yMenu_ResetSingularComponentToDefaultState() {
+ final ComponentName componentName =
+ ComponentName.createRelative("external", MENU_SERVICE_RELATIVE_CLASS_NAME);
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), any(),
+ eq(mA11yms.getCurrentUserIdLocked()))).thenReturn(
+ List.of(createResolveInfo(componentName)));
+
+ mA11yms.migrateAccessibilityMenuIfNecessaryLocked(mA11yms.getCurrentUserState());
+
+ verify(mMockPackageManager).setComponentEnabledSetting(componentName,
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ PackageManager.DONT_KILL_APP);
+ }
+
+ @Test
+ public void testMigrateA11yMenu_DoNothing_WhenNoMenuComponents() {
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), any(),
+ eq(mA11yms.getCurrentUserIdLocked()))).thenReturn(List.of());
+
+ mA11yms.migrateAccessibilityMenuIfNecessaryLocked(mA11yms.getCurrentUserState());
+
+ verify(mMockPackageManager, never()).setComponentEnabledSetting(any(),
+ anyInt(), anyInt());
+ }
+
+ @Test
+ public void testMigrateA11yMenu_DoNothing_WhenTooManyMenuComponents() {
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), any(),
+ eq(mA11yms.getCurrentUserIdLocked()))).thenReturn(List.of(
+ createResolveInfo(ComponentName.createRelative("external1",
+ MENU_SERVICE_RELATIVE_CLASS_NAME)),
+ createResolveInfo(ComponentName.createRelative("external2",
+ MENU_SERVICE_RELATIVE_CLASS_NAME)),
+ createResolveInfo(ComponentName.createRelative("external3",
+ MENU_SERVICE_RELATIVE_CLASS_NAME))));
+
+ mA11yms.migrateAccessibilityMenuIfNecessaryLocked(mA11yms.getCurrentUserState());
+
+ verify(mMockPackageManager, never()).setComponentEnabledSetting(any(),
+ anyInt(), anyInt());
+ }
+
+ @Test
+ public void testMigrateA11yMenu_DoNothing_WhenNoMenuInSystem() {
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), any(),
+ eq(mA11yms.getCurrentUserIdLocked()))).thenReturn(List.of(
+ createResolveInfo(ComponentName.createRelative("external1",
+ MENU_SERVICE_RELATIVE_CLASS_NAME)),
+ createResolveInfo(ComponentName.createRelative("external2",
+ MENU_SERVICE_RELATIVE_CLASS_NAME))));
+
+ mA11yms.migrateAccessibilityMenuIfNecessaryLocked(mA11yms.getCurrentUserState());
+
+ verify(mMockPackageManager, never()).setComponentEnabledSetting(any(),
+ anyInt(), anyInt());
+ }
+
+ @Test
+ public void testMigrateA11yMenu_PerformsMigration() {
+ final ComponentName menuOutsideSystem =
+ ComponentName.createRelative("external", MENU_SERVICE_RELATIVE_CLASS_NAME);
+ final String[] migratedSettings = {
+ ACCESSIBILITY_BUTTON_TARGETS,
+ ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ ENABLED_ACCESSIBILITY_SERVICES
+ };
+ // Start the user with Menu-outside-system enabled,
+ for (String setting : migratedSettings) {
+ Settings.Secure.putStringForUser(
+ mTestableContext.getContentResolver(),
+ setting,
+ menuOutsideSystem.flattenToShortString(),
+ mA11yms.getCurrentUserIdLocked());
+ }
+ // and both Menu versions present.
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), any(),
+ eq(mA11yms.getCurrentUserIdLocked()))).thenReturn(List.of(
+ createResolveInfo(menuOutsideSystem),
+ createResolveInfo(ACCESSIBILITY_MENU_IN_SYSTEM)));
+
+ mA11yms.migrateAccessibilityMenuIfNecessaryLocked(mA11yms.getCurrentUserState());
+
+ // Menu-outside-system should be disabled,
+ verify(mMockPackageManager).setComponentEnabledSetting(menuOutsideSystem,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ // and all settings should migrated to Menu-in-system.
+ for (String setting : migratedSettings) {
+ ComponentName componentName = ComponentName.unflattenFromString(
+ Settings.Secure.getStringForUser(
+ mTestableContext.getContentResolver(),
+ setting,
+ mA11yms.getCurrentUserIdLocked()));
+ assertThat(componentName).isEqualTo(ACCESSIBILITY_MENU_IN_SYSTEM);
+ }
+ }
+
+ private static ResolveInfo createResolveInfo(ComponentName componentName) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = new ServiceInfo();
+ resolveInfo.serviceInfo.packageName = componentName.getPackageName();
+ resolveInfo.serviceInfo.name = componentName.getClassName();
+ return resolveInfo;
+ }
+
private void mockManageAccessibilityGranted(TestableContext context) {
context.getTestablePermissions().setPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
PackageManager.PERMISSION_GRANTED);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
index c84c2c2..b5e0e07 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
@@ -28,6 +28,7 @@
import android.accessibilityservice.AccessibilityTrace;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Handler;
import android.view.accessibility.AccessibilityEvent;
@@ -35,6 +36,7 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import com.android.server.wm.WindowManagerInternal;
@@ -79,7 +81,10 @@
@Before
public void setup() {
+ final Resources resources = getInstrumentation().getContext().getResources();
MockitoAnnotations.initMocks(this);
+ when(mMockContext.getResources()).thenReturn(resources);
+
mAccessibilityServiceInfo = new AccessibilityServiceInfo();
mProxyConnection = new ProxyAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
mAccessibilityServiceInfo, CONNECTION_ID , new Handler(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index aec70f4..6216c66 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -16,16 +16,23 @@
package com.android.server.biometrics;
+import static android.Manifest.permission.MANAGE_BIOMETRIC;
+import static android.Manifest.permission.TEST_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,11 +60,14 @@
import com.android.internal.R;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Stubber;
import java.util.List;
@@ -65,11 +75,13 @@
@SmallTest
public class AuthServiceTest {
- private static final String TAG = "AuthServiceTest";
private static final String TEST_OP_PACKAGE_NAME = "test_package";
private AuthService mAuthService;
+ @Rule
+ public MockitoRule mockitorule = MockitoJUnit.rule();
+
@Mock
private Context mContext;
@Mock
@@ -97,8 +109,6 @@
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
// Placeholder test config
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
@@ -123,10 +133,13 @@
when(mInjector.getIrisService()).thenReturn(mIrisService);
when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
when(mInjector.isHidlDisabled(any())).thenReturn(false);
+
+ setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */);
}
@Test
public void testRegisterNullService_doesNotRegister() throws Exception {
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
// Config contains Fingerprint, Iris, Face, but services are all null
@@ -260,6 +273,40 @@
}
@Test
+ public void testAuthenticate_throwsWhenUsingTestConfigurations() {
+ final PromptInfo promptInfo = mock(PromptInfo.class);
+ when(promptInfo.containsPrivateApiConfigurations()).thenReturn(false);
+ when(promptInfo.containsTestConfigurations()).thenReturn(true);
+
+ testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+ }
+
+ @Test
+ public void testAuthenticate_throwsWhenUsingPrivateApis() {
+ final PromptInfo promptInfo = mock(PromptInfo.class);
+ when(promptInfo.containsPrivateApiConfigurations()).thenReturn(true);
+ when(promptInfo.containsTestConfigurations()).thenReturn(false);
+
+ testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+ }
+
+ private void testAuthenticate_throwsWhenUsingTestConfigurations(PromptInfo promptInfo) {
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ assertThrows(SecurityException.class, () -> {
+ mAuthService.mImpl.authenticate(
+ null /* token */,
+ 10 /* sessionId */,
+ 2 /* userId */,
+ mReceiver,
+ TEST_OP_PACKAGE_NAME,
+ promptInfo);
+ waitForIdle();
+ });
+ }
+
+ @Test
public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
@@ -283,8 +330,10 @@
}
@Test
- public void testHasEnrolledBiometrics_callsBiometricServiceHasEnrolledBiometrics() throws
- Exception {
+ public void testHasEnrolledBiometrics_callsBiometricServiceHasEnrolledBiometrics()
+ throws Exception {
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
@@ -307,6 +356,8 @@
@Test
public void testRegisterKeyguardCallback_callsBiometricServiceRegisterKeyguardCallback()
throws Exception {
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
@@ -320,6 +371,20 @@
eq(callback), eq(UserHandle.getCallingUserId()));
}
+ private static void setInternalAndTestBiometricPermissions(
+ Context context, boolean hasPermission) {
+ for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) {
+ when(context.checkCallingPermission(eq(p))).thenReturn(hasPermission
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+ when(context.checkCallingOrSelfPermission(eq(p))).thenReturn(hasPermission
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+ final Stubber doPermCheck =
+ hasPermission ? doNothing() : doThrow(SecurityException.class);
+ doPermCheck.when(context).enforceCallingPermission(eq(p), any());
+ doPermCheck.when(context).enforceCallingOrSelfPermission(eq(p), any());
+ }
+ }
+
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java
new file mode 100644
index 0000000..a488ab4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CallMetadataSyncInCallServiceTest {
+
+ private CallMetadataSyncInCallService mSyncInCallService;
+ @Mock
+ private CrossDeviceCall mMockCrossDeviceCall;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mSyncInCallService = new CallMetadataSyncInCallService();
+ }
+
+ @Test
+ public void getCallForId_invalid() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(-1L);
+ final CrossDeviceCall call = mSyncInCallService.getCallForId(-1L,
+ List.of(mMockCrossDeviceCall));
+ assertWithMessage("Unexpectedly found a match for call id").that(call).isNull();
+ }
+
+ @Test
+ public void getCallForId_noMatch() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+ final CrossDeviceCall call = mSyncInCallService.getCallForId(1L,
+ List.of(mMockCrossDeviceCall));
+ assertWithMessage("Unexpectedly found a match for call id").that(call).isNull();
+ }
+
+ @Test
+ public void getCallForId_hasMatch() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+ final CrossDeviceCall call = mSyncInCallService.getCallForId(5L,
+ List.of(mMockCrossDeviceCall));
+ assertWithMessage("Unexpectedly did not find a match for call id").that(call).isNotNull();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index c1ee88c..760ed9b 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -18,12 +18,12 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.hardware.display.DisplayManagerInternal;
@@ -133,14 +133,14 @@
@Test
public void unregisterInputDevice_anotherMouseExists_setPointerDisplayIdOverride() {
final IBinder deviceToken = new Binder();
- mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
+ mInputController.createMouse("mouse1", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
- verify(mNativeWrapperMock).openUinputMouse(eq("name"), eq(1), eq(1), anyString());
+ verify(mNativeWrapperMock).openUinputMouse(eq("mouse1"), eq(1), eq(1), anyString());
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
final IBinder deviceToken2 = new Binder();
- mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken2,
+ mInputController.createMouse("mouse2", /*vendorId= */ 1, /*productId= */ 1, deviceToken2,
/* displayId= */ 2);
- verify(mNativeWrapperMock, times(2)).openUinputMouse(eq("name"), eq(1), eq(1), anyString());
+ verify(mNativeWrapperMock).openUinputMouse(eq("mouse2"), eq(1), eq(1), anyString());
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2));
mInputController.unregisterInputDevice(deviceToken);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
@@ -193,4 +193,68 @@
mInputController.unregisterInputDevice(deviceToken);
verify(mInputManagerInternalMock).removeKeyboardLayoutAssociation(anyString());
}
+
+ @Test
+ public void createInputDevice_tooLongNameRaisesException() {
+ final IBinder deviceToken = new Binder("device");
+ // The underlying uinput implementation only supports device names up to 80 bytes. This
+ // string is all ASCII characters, therefore if we have more than 80 ASCII characters we
+ // will have more than 80 bytes.
+ String deviceName =
+ "This.is.a.very.long.device.name.that.exceeds.the.maximum.length.of.80.bytes"
+ + ".by.a.couple.bytes";
+
+ assertThrows(RuntimeException.class, () -> {
+ mInputController.createDpad(deviceName, /*vendorId= */3, /*productId=*/3, deviceToken,
+ 1);
+ });
+ }
+
+ @Test
+ public void createInputDevice_tooLongDeviceNameRaisesException() {
+ final IBinder deviceToken = new Binder("device");
+ // The underlying uinput implementation only supports device names up to 80 bytes (including
+ // a 0-byte terminator).
+ // This string is 79 characters and 80 bytes (including the 0-byte terminator)
+ String deviceName =
+ "This.is.a.very.long.device.name.that.exceeds.the.maximum.length01234567890123456";
+
+ assertThrows(RuntimeException.class, () -> {
+ mInputController.createDpad(deviceName, /*vendorId= */3, /*productId=*/3, deviceToken,
+ 1);
+ });
+ }
+
+ @Test
+ public void createInputDevice_stringWithLessThanMaxCharsButMoreThanMaxBytesRaisesException() {
+ final IBinder deviceToken = new Binder("device1");
+
+ // Has only 39 characters but is 109 bytes as utf-8
+ String device_name =
+ "░▄▄▄▄░\n" +
+ "▀▀▄██►\n" +
+ "▀▀███►\n" +
+ "░▀███►░█►\n" +
+ "▒▄████▀▀";
+
+ assertThrows(RuntimeException.class, () -> {
+ mInputController.createDpad(device_name, /*vendorId= */5, /*productId=*/5,
+ deviceToken, 1);
+ });
+ }
+
+ @Test
+ public void createInputDevice_duplicateNamesAreNotAllowed() {
+ final IBinder deviceToken1 = new Binder("deviceToken1");
+ final IBinder deviceToken2 = new Binder("deviceToken2");
+
+ final String sharedDeviceName = "DeviceName";
+
+ mInputController.createDpad(sharedDeviceName, /*vendorId= */4, /*productId=*/4,
+ deviceToken1, 1);
+ assertThrows("Device names need to be unique", RuntimeException.class, () -> {
+ mInputController.createDpad(sharedDeviceName, /*vendorId= */5, /*productId=*/5,
+ deviceToken2, 2);
+ });
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index dad7977..ce5ddb0 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -135,7 +135,9 @@
private static final String VENDING_PACKAGE_NAME = "com.android.vending";
private static final String GOOGLE_DIALER_PACKAGE_NAME = "com.google.android.dialer";
private static final String GOOGLE_MAPS_PACKAGE_NAME = "com.google.android.apps.maps";
- private static final String DEVICE_NAME = "device name";
+ private static final String DEVICE_NAME_1 = "device name 1";
+ private static final String DEVICE_NAME_2 = "device name 2";
+ private static final String DEVICE_NAME_3 = "device name 3";
private static final int DISPLAY_ID_1 = 2;
private static final int DISPLAY_ID_2 = 3;
private static final int DEVICE_OWNER_UID_1 = 50;
@@ -160,14 +162,14 @@
new VirtualDpadConfig.Builder()
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
private static final VirtualKeyboardConfig KEYBOARD_CONFIG =
new VirtualKeyboardConfig.Builder()
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.setLanguageTag(VirtualKeyboardConfig.DEFAULT_LANGUAGE_TAG)
.setLayoutType(VirtualKeyboardConfig.DEFAULT_LAYOUT_TYPE)
@@ -176,21 +178,21 @@
new VirtualMouseConfig.Builder()
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
private static final VirtualTouchscreenConfig TOUCHSCREEN_CONFIG =
new VirtualTouchscreenConfig.Builder(WIDTH, HEIGHT)
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
private static final VirtualNavigationTouchpadConfig NAVIGATION_TOUCHPAD_CONFIG =
new VirtualNavigationTouchpadConfig.Builder(WIDTH, HEIGHT)
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
private static final String TEST_SITE = "http://test";
@@ -549,7 +551,7 @@
new VirtualKeyboardConfig.Builder()
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.setLanguageTag("zh-CN")
.build();
@@ -557,7 +559,7 @@
new VirtualKeyboardConfig.Builder()
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_2)
.setAssociatedDisplayId(DISPLAY_ID_2)
.setLanguageTag("fr-FR")
.build();
@@ -784,7 +786,7 @@
/* touchscrenWidth= */ 600, /* touchscreenHeight= */ 800)
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
mDeviceImpl.createVirtualTouchscreen(positiveConfig, BINDER);
@@ -822,7 +824,7 @@
/* touchpadHeight= */ 50, /* touchpadWidth= */ 50)
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
mDeviceImpl.createVirtualNavigationTouchpad(positiveConfig, BINDER);
@@ -893,7 +895,7 @@
() -> mDeviceImpl.createVirtualSensor(
BINDER,
new VirtualSensorConfig.Builder(
- Sensor.TYPE_ACCELEROMETER, DEVICE_NAME).build()));
+ Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1).build()));
}
}
@@ -920,7 +922,7 @@
mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER);
assertWithMessage("Virtual dpad should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
- verify(mNativeWrapperMock).openUinputDpad(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID),
+ verify(mNativeWrapperMock).openUinputDpad(eq(DEVICE_NAME_1), eq(VENDOR_ID), eq(PRODUCT_ID),
anyString());
}
@@ -930,7 +932,7 @@
mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
- verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
+ verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME_1), eq(VENDOR_ID),
eq(PRODUCT_ID), anyString());
}
@@ -941,7 +943,7 @@
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.getInputDeviceDescriptors())
.isNotEmpty();
- verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
+ verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME_1), eq(VENDOR_ID),
eq(PRODUCT_ID), anyString());
assertThat(mDeviceImpl.getDeviceLocaleList()).isEqualTo(
LocaleList.forLanguageTags(KEYBOARD_CONFIG.getLanguageTag()));
@@ -953,7 +955,7 @@
new VirtualKeyboardConfig.Builder()
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
@@ -962,7 +964,7 @@
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.getInputDeviceDescriptors())
.isNotEmpty();
- verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
+ verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME_1), eq(VENDOR_ID),
eq(PRODUCT_ID), anyString());
assertThat(mDeviceImpl.getDeviceLocaleList()).isEqualTo(
LocaleList.forLanguageTags(VirtualKeyboardConfig.DEFAULT_LANGUAGE_TAG));
@@ -982,7 +984,7 @@
mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER);
assertWithMessage("Virtual mouse should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
- verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID),
+ verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME_1), eq(VENDOR_ID), eq(PRODUCT_ID),
anyString());
}
@@ -992,7 +994,7 @@
mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER);
assertWithMessage("Virtual touchscreen should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
- verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
+ verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME_1), eq(VENDOR_ID),
eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
}
@@ -1003,15 +1005,16 @@
assertWithMessage("Virtual navigation touchpad should register fd when the display matches")
.that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
- verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
+ verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME_1), eq(VENDOR_ID),
eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
}
@Test
public void createVirtualKeyboard_inputDeviceId_obtainFromInputController() {
final int fd = 1;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */ 1, /* displayId= */ 1, PHYS,
- INPUT_DEVICE_ID);
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS,
+ DEVICE_NAME_1, INPUT_DEVICE_ID);
assertWithMessage(
"InputController should return device id from InputDeviceDescriptor").that(
mInputController.getInputDeviceId(BINDER)).isEqualTo(INPUT_DEVICE_ID);
@@ -1052,7 +1055,7 @@
@Test
public void close_cleanSensorController() {
mSensorController.addSensorForTesting(
- BINDER, SENSOR_HANDLE, Sensor.TYPE_ACCELEROMETER, DEVICE_NAME);
+ BINDER, SENSOR_HANDLE, Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1);
mDeviceImpl.close();
@@ -1085,8 +1088,9 @@
final int fd = 1;
final int keyCode = KeyEvent.KEYCODE_A;
final int action = VirtualKeyEvent.ACTION_UP;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */1, /* displayId= */ 1, PHYS,
- INPUT_DEVICE_ID);
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS,
+ DEVICE_NAME_1, INPUT_DEVICE_ID);
mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
.setKeyCode(keyCode)
@@ -1112,9 +1116,10 @@
final int fd = 1;
final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
- INPUT_DEVICE_ID);
- doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS,
+ DEVICE_NAME_1, INPUT_DEVICE_ID);
+ doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
.setAction(action).build());
@@ -1126,7 +1131,8 @@
final int fd = 1;
final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
assertThrows(
IllegalStateException.class,
@@ -1151,9 +1157,10 @@
final int fd = 1;
final float x = -0.2f;
final float y = 0.7f;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
- doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
+ doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
.setRelativeX(x).setRelativeY(y).build());
verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
@@ -1164,7 +1171,8 @@
final int fd = 1;
final float x = -0.2f;
final float y = 0.7f;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
assertThrows(
IllegalStateException.class,
@@ -1190,9 +1198,10 @@
final int fd = 1;
final float x = 0.5f;
final float y = 1f;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
- doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
+ doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
.setYAxisMovement(y).build());
@@ -1204,7 +1213,8 @@
final int fd = 1;
final float x = 0.5f;
final float y = 1f;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
assertThrows(
IllegalStateException.class,
@@ -1236,8 +1246,9 @@
final float x = 100.5f;
final float y = 200.5f;
final int action = VirtualTouchEvent.ACTION_UP;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */3, /* displayId= */ 1, PHYS,
- INPUT_DEVICE_ID);
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
+ DEVICE_NAME_1, INPUT_DEVICE_ID);
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
.setX(x)
.setY(y)
@@ -1259,8 +1270,9 @@
final int action = VirtualTouchEvent.ACTION_UP;
final float pressure = 1.0f;
final float majorAxisSize = 10.0f;
- mInputController.addDeviceForTesting(BINDER, fd, /* type= */3, /* displayId= */ 1, PHYS,
- INPUT_DEVICE_ID);
+ mInputController.addDeviceForTesting(BINDER, fd,
+ InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
+ DEVICE_NAME_1, INPUT_DEVICE_ID);
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
.setX(x)
.setY(y)
@@ -1281,19 +1293,19 @@
mDeviceImpl.mVirtualDisplayIds.add(3);
VirtualMouseConfig config1 = new VirtualMouseConfig.Builder()
.setAssociatedDisplayId(1)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_1)
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
.build();
VirtualMouseConfig config2 = new VirtualMouseConfig.Builder()
.setAssociatedDisplayId(2)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_2)
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
.build();
VirtualMouseConfig config3 = new VirtualMouseConfig.Builder()
.setAssociatedDisplayId(3)
- .setInputDeviceName(DEVICE_NAME)
+ .setInputDeviceName(DEVICE_NAME_3)
.setVendorId(VENDOR_ID)
.setProductId(PRODUCT_ID)
.build();
@@ -1595,6 +1607,33 @@
verify(mSoundEffectListener).onPlaySoundEffect(AudioManager.FX_KEY_CLICK);
}
+ @Test
+ public void getDisplayIdsForDevice_invalidDeviceId_emptyResult() {
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_2);
+ assertThat(displayIds).isEmpty();
+ }
+
+ @Test
+ public void getDisplayIdsForDevice_noDisplays_emptyResult() {
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
+ assertThat(displayIds).isEmpty();
+ }
+
+ @Test
+ public void getDisplayIdsForDevice_oneDisplay_resultContainsCorrectDisplayId() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
+ assertThat(displayIds).containsExactly(DISPLAY_ID_1);
+ }
+
+ @Test
+ public void getDisplayIdsForDevice_twoDisplays_resultContainsCorrectDisplayIds() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_2);
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
+ assertThat(displayIds).containsExactly(DISPLAY_ID_1, DISPLAY_ID_2);
+ }
+
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid) {
VirtualDeviceParams params = new VirtualDeviceParams.Builder()
.setBlockedActivities(getBlockedActivities())
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
index 0dd60b8..0344663 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
@@ -40,8 +40,6 @@
import androidx.test.core.app.ApplicationProvider;
-import com.android.server.job.JobSchedulerInternal;
-
import junit.framework.TestCase;
import org.jetbrains.annotations.NotNull;
@@ -49,8 +47,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-
/**
* Tests for SyncManager.
*
@@ -69,8 +65,6 @@
private UserManager mUserManager;
@Mock
private AccountManagerInternal mAccountManagerInternal;
- @Mock
- private JobSchedulerInternal mJobSchedulerInternal;
private class SyncManagerWithMockedServices extends SyncManager {
@@ -79,11 +73,6 @@
return mAccountManagerInternal;
}
- @Override
- protected JobSchedulerInternal getJobSchedulerInternal() {
- return mJobSchedulerInternal;
- }
-
private SyncManagerWithMockedServices(Context context, boolean factoryTest) {
super(context, factoryTest);
}
@@ -95,7 +84,6 @@
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
doNothing().when(mAccountManagerInternal).addOnAppPermissionChangeListener(any());
- when(mJobSchedulerInternal.getSystemScheduledPendingJobs()).thenReturn(new ArrayList<>());
mSyncManager = spy(new SyncManagerWithMockedServices(mContext, true));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 673b696..b5c5582 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -59,7 +59,6 @@
@RunWith(AndroidJUnit4.class)
public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
- private static final String USER_TYPE_EMPTY = "";
private static final int COPE_ADMIN1_APP_ID = 123;
private static final int COPE_ANOTHER_ADMIN_APP_ID = 125;
private static final int COPE_PROFILE_USER_ID = 11;
@@ -81,6 +80,7 @@
when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(true);
+ when(getServices().userManagerInternal.getUserIds()).thenReturn(new int[]{0});
}
// Test setting default restrictions for managed profile.
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 4998a6c..a58c4de 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -134,6 +134,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.security.KeyChain;
import android.security.keystore.AttestationUtils;
@@ -259,6 +260,8 @@
private static final String PROFILE_OFF_SUSPENSION_TITLE = "suspension_title";
private static final String PROFILE_OFF_SUSPENSION_TEXT = "suspension_text";
private static final String PROFILE_OFF_SUSPENSION_SOON_TEXT = "suspension_tomorrow_text";
+ private static final String FLAG_ENABLE_WORK_PROFILE_TELEPHONY =
+ "enable_work_profile_telephony";
@Before
public void setUp() throws Exception {
@@ -270,6 +273,7 @@
mContext = getContext();
mServiceContext = mContext;
mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ when(getServices().userManagerInternal.getUserIds()).thenReturn(new int[]{0});
when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(true);
doReturn(Collections.singletonList(new ResolveInfo()))
@@ -4982,7 +4986,8 @@
public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
-
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "true", false);
// Even if the caller is the managed profile, the current user is the user 0
when(getServices().iactivityManager.getCurrentUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
@@ -5043,6 +5048,8 @@
verify(getServices().packageManagerInternal)
.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index c739969..ded8ad5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -203,6 +203,11 @@
}
@Override
+ public String getOpPackageName() {
+ return getPackageName();
+ }
+
+ @Override
public ApplicationInfo getApplicationInfo() {
if (applicationInfo != null) {
return applicationInfo;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
index 1779b16..1d6846c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
@@ -220,7 +220,7 @@
// DO should be marked as able to grant sensors permission during upgrade and should be
// reported as such via the API.
- assertThat(dpms.canAdminGrantSensorsPermissionsForUser(ownerUser)).isTrue();
+ assertThat(dpms.canAdminGrantSensorsPermissions()).isTrue();
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index ddde385..fc25152 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -484,6 +484,7 @@
// Sensor reads 100 lux. We should get max brightness.
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
// Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
final float throttledBrightness = 0.123f;
@@ -494,6 +495,8 @@
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
/* shouldResetShortTermModel= */ true);
assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
+ // The raw brightness value should not have throttling applied
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
// Remove throttling and notify ABC again
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -503,6 +506,7 @@
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
/* shouldResetShortTermModel= */ true);
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 82b89bb..0ea20a8 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -32,6 +33,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.server.display.config.ThermalStatus;
import org.junit.Before;
import org.junit.Test;
@@ -43,6 +45,8 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -164,7 +168,56 @@
assertArrayEquals(new int[]{-1, 10, 20, 30, 40},
mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
- // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
+ List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+ new ArrayList();
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.4f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.3f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.2f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.1f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.05f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.025f
+ ));
+ assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
+ mDisplayDeviceConfig.getBrightnessThrottlingData());
+
+ throttlingLevels.clear();
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.2f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.15f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.1f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.05f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.025f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.0125f
+ ));
+ assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
+ mDisplayDeviceConfig.getConcurrentDisplaysBrightnessThrottlingData());
+
+ assertNotNull(mDisplayDeviceConfig.getHostUsiVersion());
+ assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMajorVersion(), 2);
+ assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMinorVersion(), 0);
+
+ // Todo: Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
@@ -236,7 +289,7 @@
assertArrayEquals(mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(),
HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE);
- // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
+ // Todo: Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
@@ -428,16 +481,60 @@
+ "<ambientLightHorizonShort>50</ambientLightHorizonShort>\n"
+ "<screenBrightnessRampIncreaseMaxMillis>"
+ "2000"
- + "</screenBrightnessRampIncreaseMaxMillis>\n"
+ + "</screenBrightnessRampIncreaseMaxMillis>\n"
+ "<thermalThrottling>\n"
+ "<brightnessThrottlingMap>\n"
+ "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>light</thermalStatus>\n"
+ + "<brightness>0.4</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>moderate</thermalStatus>\n"
+ + "<brightness>0.3</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>severe</thermalStatus>\n"
+ + "<brightness>0.2</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>critical</thermalStatus>\n"
+ + "<brightness>0.1</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ "<thermalStatus>emergency</thermalStatus>\n"
- + "<!-- Throttling to 250 nits: (250-2.0)/(500-2.0)*(0.62-0.0)+0"
- + ".0 = 0.30875502 -->\n"
- + "<brightness>0.30875502</brightness>\n"
+ + "<brightness>0.05</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>shutdown</thermalStatus>\n"
+ + "<brightness>0.025</brightness>\n"
+ "</brightnessThrottlingPoint>\n"
+ "</brightnessThrottlingMap>\n"
+ + "<concurrentDisplaysBrightnessThrottlingMap>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>light</thermalStatus>\n"
+ + "<brightness>0.2</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>moderate</thermalStatus>\n"
+ + "<brightness>0.15</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>severe</thermalStatus>\n"
+ + "<brightness>0.1</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>critical</thermalStatus>\n"
+ + "<brightness>0.05</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>emergency</thermalStatus>\n"
+ + "<brightness>0.025</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>shutdown</thermalStatus>\n"
+ + "<brightness>0.0125</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "</concurrentDisplaysBrightnessThrottlingMap>\n"
+ "</thermalThrottling>\n"
+ "<refreshRate>\n"
+ "<lowerBlockingZoneConfigs>\n"
@@ -478,6 +575,10 @@
+ "<item>30</item>\n"
+ "<item>40</item>\n"
+ "</screenOffBrightnessSensorValueToLux>\n"
+ + "<usiVersion>\n"
+ + "<majorVersion>2</majorVersion>\n"
+ + "<minorVersion>0</minorVersion>\n"
+ + "</usiVersion>\n"
+ "</displayConfiguration>\n";
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 69a0b87..3a3a507 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -298,6 +298,18 @@
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(0, 60 - error));
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(60 + error, Float.POSITIVE_INFINITY));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
new file mode 100644
index 0000000..24fc348
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HbmEventTest {
+ private long mStartTimeMillis;
+ private long mEndTimeMillis;
+ private HbmEvent mHbmEvent;
+
+ @Before
+ public void setUp() {
+ mStartTimeMillis = 10;
+ mEndTimeMillis = 20;
+ mHbmEvent = new HbmEvent(mStartTimeMillis, mEndTimeMillis);
+ }
+
+ @Test
+ public void getCorrectValues() {
+ assertEquals(mHbmEvent.getStartTimeMillis(), mStartTimeMillis);
+ assertEquals(mHbmEvent.getEndTimeMillis(), mEndTimeMillis);
+ }
+
+ @Test
+ public void toStringGeneratesExpectedString() {
+ String actualString = mHbmEvent.toString();
+ String expectedString = "HbmEvent: {startTimeMillis:" + mStartTimeMillis
+ + ", endTimeMillis: " + mEndTimeMillis + "}, total: "
+ + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
+ assertEquals(actualString, expectedString);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index a1e5ce7..2655c3f 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -96,6 +96,7 @@
private Binder mDisplayToken;
private String mDisplayUniqueId;
private Context mContextSpy;
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -118,6 +119,7 @@
mTestLooper = new TestLooper(mClock::now);
mDisplayToken = null;
mDisplayUniqueId = "unique_id";
+
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -134,7 +136,8 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
+ null, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@@ -144,7 +147,8 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
+ null, mContextSpy);
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -699,9 +703,12 @@
// Creates instance with standard initialization values.
private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
initHandler(clock);
+ if (mHighBrightnessModeMetadata == null) {
+ mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
+ }
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
- DEFAULT_HBM_DATA, null, () -> {}, mContextSpy);
+ DEFAULT_HBM_DATA, null, () -> {}, mHighBrightnessModeMetadata, mContextSpy);
}
private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
new file mode 100644
index 0000000..ede54e0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HighBrightnessModeMetadataTest {
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
+
+ private long mRunningStartTimeMillis = -1;
+
+ @Before
+ public void setUp() {
+ mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
+ }
+
+ @Test
+ public void checkDefaultValues() {
+ assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
+ mRunningStartTimeMillis);
+ assertEquals(mHighBrightnessModeMetadata.getHbmEventQueue().size(), 0);
+ }
+
+ @Test
+ public void checkSetValues() {
+ mRunningStartTimeMillis = 10;
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(mRunningStartTimeMillis);
+ assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
+ mRunningStartTimeMillis);
+ HbmEvent expectedHbmEvent = new HbmEvent(10, 20);
+ mHighBrightnessModeMetadata.addHbmEvent(expectedHbmEvent);
+ HbmEvent actualHbmEvent = mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst();
+ assertEquals(expectedHbmEvent.toString(), actualHbmEvent.toString());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 5ef762b..a860387 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.DEFAULT_DISPLAY_GROUP;
import static android.view.Display.TYPE_EXTERNAL;
@@ -537,6 +538,11 @@
/* isOverrideActive= */false,
/* isInteractive= */true,
/* isBootCompleted= */true));
+ assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
+ INVALID_DEVICE_STATE,
+ /* isOverrideActive= */false,
+ /* isInteractive= */true,
+ /* isBootCompleted= */true));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
index d332b30..c0c63c6 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -30,6 +30,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class BrightnessEventTest {
+ private static final String DISPLAY_BRIGHTNESS_STRATEGY_NAME = "strategy_name";
private BrightnessEvent mBrightnessEvent;
@Before
@@ -53,6 +54,7 @@
mBrightnessEvent.setFlags(0);
mBrightnessEvent.setAdjustmentFlags(0);
mBrightnessEvent.setAutomaticBrightnessEnabled(true);
+ mBrightnessEvent.setDisplayBrightnessStrategyName(DISPLAY_BRIGHTNESS_STRATEGY_NAME);
}
@Test
@@ -70,7 +72,8 @@
"BrightnessEvent: disp=1, physDisp=test, brt=0.6, initBrt=25.0, rcmdBrt=0.6,"
+ " preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off, rbcStrength=-1,"
+ " thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true, flags=,"
- + " reason=doze [ low_pwr ], autoBrightness=true";
+ + " reason=doze [ low_pwr ], autoBrightness=true, strategy="
+ + DISPLAY_BRIGHTNESS_STRATEGY_NAME;
assertEquals(expectedString, actualString);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index 57aa61a..e58b3e8 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -65,7 +65,7 @@
@Test
public void setReasonDoesntSetIfModifierIsBeyondExtremes() {
- int extremeReason = 10;
+ int extremeReason = BrightnessReason.REASON_MAX + 1;
mBrightnessReason.setReason(extremeReason);
assertEquals(mBrightnessReason.getReason(), BrightnessReason.REASON_DOZE);
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c24d83f..d4ab794 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -75,7 +75,7 @@
}
@Test
- public void updateBrightness() {
+ public void testUpdateBrightness() {
DisplayPowerRequest displayPowerRequest = mock(DisplayPowerRequest.class);
DisplayBrightnessStrategy displayBrightnessStrategy = mock(DisplayBrightnessStrategy.class);
int targetDisplayState = Display.STATE_DOZE;
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index dcf217c..a9e616d 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -31,6 +31,7 @@
import com.android.internal.R;
import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
import com.android.server.display.brightness.strategy.OverrideBrightnessStrategy;
import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrategy;
@@ -61,6 +62,8 @@
@Mock
private InvalidBrightnessStrategy mInvalidBrightnessStrategy;
@Mock
+ private FollowerBrightnessStrategy mFollowerBrightnessStrategy;
+ @Mock
private Context mContext;
@Mock
private Resources mResources;
@@ -100,6 +103,11 @@
}
@Override
+ FollowerBrightnessStrategy getFollowerBrightnessStrategy(int displayId) {
+ return mFollowerBrightnessStrategy;
+ }
+
+ @Override
InvalidBrightnessStrategy getInvalidBrightnessStrategy() {
return mInvalidBrightnessStrategy;
}
@@ -133,6 +141,7 @@
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.screenBrightnessOverride = 0.4f;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
Display.STATE_ON), mOverrideBrightnessStrategy);
}
@@ -142,6 +151,7 @@
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(0.3f);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
Display.STATE_ON), mTemporaryBrightnessStrategy);
@@ -152,6 +162,7 @@
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.boostScreenBrightness = true;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
displayPowerRequest.screenBrightnessOverride = Float.NaN;
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
@@ -163,8 +174,18 @@
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
Display.STATE_ON), mInvalidBrightnessStrategy);
}
+
+ @Test
+ public void selectStrategySelectsFollowerStrategyWhenValid() {
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f);
+ assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
+ Display.STATE_ON), mFollowerBrightnessStrategy);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index 431a239..c434631 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
@@ -44,7 +44,7 @@
}
@Test
- public void updateBrightnessWorksAsExpectedWhenBoostBrightnessIsRequested() {
+ public void testUpdateBrightnessWhenBoostBrightnessIsRequested() {
DisplayManagerInternal.DisplayPowerRequest
displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
displayPowerRequest.boostScreenBrightness = true;
@@ -55,6 +55,7 @@
.setBrightness(PowerManager.BRIGHTNESS_MAX)
.setBrightnessReason(brightnessReason)
.setSdrBrightness(PowerManager.BRIGHTNESS_MAX)
+ .setDisplayBrightnessStrategyName(mBoostBrightnessStrategy.getName())
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mBoostBrightnessStrategy.updateBrightness(displayPowerRequest);
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index 29652ff..d60caf6 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
@@ -41,7 +41,7 @@
}
@Test
- public void updateBrightnessWorksAsExpectedWhenScreenDozeStateIsRequested() {
+ public void testUpdateBrightnessWhenScreenDozeStateIsRequested() {
DisplayPowerRequest displayPowerRequest = new DisplayPowerRequest();
float dozeScreenBrightness = 0.2f;
displayPowerRequest.dozeScreenBrightness = dozeScreenBrightness;
@@ -52,6 +52,7 @@
.setBrightness(dozeScreenBrightness)
.setBrightnessReason(brightnessReason)
.setSdrBrightness(dozeScreenBrightness)
+ .setDisplayBrightnessStrategyName(mDozeBrightnessModeStrategy.getName())
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mDozeBrightnessModeStrategy.updateBrightness(displayPowerRequest);
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
new file mode 100644
index 0000000..081f19d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FollowerBrightnessStrategyTest {
+ private FollowerBrightnessStrategy mFollowerBrightnessStrategy;
+
+ @Before
+ public void before() {
+ mFollowerBrightnessStrategy = new FollowerBrightnessStrategy(Display.DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testUpdateBrightness() {
+ DisplayManagerInternal.DisplayPowerRequest
+ displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
+ float brightnessToFollow = 0.2f;
+ mFollowerBrightnessStrategy.setBrightnessToFollow(brightnessToFollow);
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_FOLLOWER);
+ DisplayBrightnessState expectedDisplayBrightnessState =
+ new DisplayBrightnessState.Builder()
+ .setBrightness(brightnessToFollow)
+ .setBrightnessReason(brightnessReason)
+ .setSdrBrightness(brightnessToFollow)
+ .setDisplayBrightnessStrategyName(mFollowerBrightnessStrategy.getName())
+ .build();
+ DisplayBrightnessState updatedDisplayBrightnessState =
+ mFollowerBrightnessStrategy.updateBrightness(displayPowerRequest);
+ assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState);
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index 4d89c28..530245d 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -43,7 +43,7 @@
}
@Test
- public void updateBrightnessWorksAsExpectedWhenScreenDozeStateIsRequested() {
+ public void testUpdateBrightnessWhenScreenDozeStateIsRequested() {
DisplayManagerInternal.DisplayPowerRequest
displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
float overrideBrightness = 0.2f;
@@ -55,6 +55,7 @@
.setBrightness(overrideBrightness)
.setBrightnessReason(brightnessReason)
.setSdrBrightness(overrideBrightness)
+ .setDisplayBrightnessStrategyName(mOverrideBrightnessStrategy.getName())
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mOverrideBrightnessStrategy.updateBrightness(displayPowerRequest);
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index 0505475..7147aa8 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
@@ -43,7 +43,7 @@
}
@Test
- public void updateBrightnessWorksAsExpectedWhenScreenOffDisplayState() {
+ public void testUpdateBrightnessWhenScreenOffDisplayState() {
DisplayPowerRequest displayPowerRequest = new DisplayPowerRequest();
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(BrightnessReason.REASON_SCREEN_OFF);
@@ -52,6 +52,8 @@
.setBrightness(PowerManager.BRIGHTNESS_OFF_FLOAT)
.setSdrBrightness(PowerManager.BRIGHTNESS_OFF_FLOAT)
.setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(mScreenOffBrightnessModeStrategy
+ .getName())
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mScreenOffBrightnessModeStrategy.updateBrightness(displayPowerRequest);
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index b92aa9c..9830edb 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
@@ -43,7 +43,7 @@
}
@Test
- public void updateBrightnessWorksAsExpectedWhenTemporaryBrightnessIsSet() {
+ public void testUpdateBrightnessWhenTemporaryBrightnessIsSet() {
DisplayManagerInternal.DisplayPowerRequest
displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
float temporaryBrightness = 0.2f;
@@ -55,6 +55,7 @@
.setBrightness(temporaryBrightness)
.setBrightnessReason(brightnessReason)
.setSdrBrightness(temporaryBrightness)
+ .setDisplayBrightnessStrategyName(mTemporaryBrightnessStrategy.getName())
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mTemporaryBrightnessStrategy.updateBrightness(displayPowerRequest);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeControlTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeControlTest.java
index 93b151e..9f295b8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeControlTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeControlTest.java
@@ -175,7 +175,11 @@
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index 4d8d25a..9c1b670 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -137,11 +137,17 @@
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_1,
- true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_1)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
hdmiPortInfos[1] =
- new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_2,
- true, false, false);
+ new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_2)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index bb50a89..3bde665 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -112,7 +112,11 @@
public HdmiPortInfo[] nativeGetPortInfos() {
if (mHdmiPortInfo == null) {
mHdmiPortInfo = new HdmiPortInfo[1];
- mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+ mHdmiPortInfo[0] = new HdmiPortInfo.Builder(1, 1, 0x1000)
+ .setCecSupported(true)
+ .setMhlSupported(true)
+ .setArcSupported(true)
+ .build();
}
return mHdmiPortInfo;
}
@@ -138,6 +142,14 @@
return isConnected == null ? false : isConnected;
}
+ @Override
+ public void nativeSetHpdSignalType(int signal, int portId) {}
+
+ @Override
+ public int nativeGetHpdSignalType(int portId) {
+ return Constants.HDMI_HPD_TYPE_PHYSICAL;
+ }
+
public void setPortConnectionStatus(int port, boolean connected) {
mPortConnectionStatus.put(port, connected);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index f27587e..e3d9558 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -118,7 +118,11 @@
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 90acc99..f5c0f2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -197,17 +197,29 @@
mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
mHdmiPortInfo = new HdmiPortInfo[4];
mHdmiPortInfo[0] =
- new HdmiPortInfo(
- 0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS, true, false, false);
+ new HdmiPortInfo.Builder(0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mHdmiPortInfo[1] =
- new HdmiPortInfo(
- 2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS, true, false, false);
+ new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mHdmiPortInfo[2] =
- new HdmiPortInfo(
- 1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mHdmiPortInfo[3] =
- new HdmiPortInfo(
- 4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
+ new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index dfab207..beba9c6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -150,7 +150,11 @@
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
mHdmiControlService.initService();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 3796ce9..9c5c0d4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -189,7 +189,11 @@
mLocalDevices.add(mHdmiLocalDevice);
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
mHdmiControlService.initService();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index cb1e78b..ccfc2b9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -193,10 +193,19 @@
mHdmiControlService.setEarcController(mHdmiEarcController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
- hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false, false);
+ hdmiPortInfos[0] = new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x1000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .setEarcSupported(false)
+ .build();
hdmiPortInfos[1] =
- new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true, true);
+ new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(true)
+ .setEarcSupported(true)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index a82a79f..d341153 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -92,15 +92,35 @@
mHdmiPortInfo = new HdmiPortInfo[5];
mHdmiPortInfo[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x2100)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mHdmiPortInfo[1] =
- new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
+ new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2200)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mHdmiPortInfo[2] =
- new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+ new HdmiPortInfo.Builder(3, HdmiPortInfo.PORT_INPUT, 0x2000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mHdmiPortInfo[3] =
- new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
+ new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, 0x3000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mHdmiPortInfo[4] =
- new HdmiPortInfo(5, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ new HdmiPortInfo.Builder(5, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiCecNetwork.initPortInfo();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 8e5bb13..55e8b20 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -99,7 +99,11 @@
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
mHdmiControlService.initService();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index cd6dfbf..8baad61 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -139,13 +139,33 @@
mLocalDevices.add(mPlaybackDeviceSpy);
mHdmiPortInfo = new HdmiPortInfo[4];
mHdmiPortInfo[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x2100)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .setEarcSupported(false)
+ .build();
mHdmiPortInfo[1] =
- new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false, false);
+ new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2200)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .setEarcSupported(false)
+ .build();
mHdmiPortInfo[2] =
- new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true, true);
+ new HdmiPortInfo.Builder(3, HdmiPortInfo.PORT_INPUT, 0x2000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(true)
+ .setEarcSupported(true)
+ .build();
mHdmiPortInfo[3] =
- new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false, false);
+ new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, 0x3000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .setEarcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlServiceSpy.initService();
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index a623841..89743cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -102,9 +102,17 @@
mTestLooper.dispatchAll();
HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[2];
hdmiPortInfo[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x1000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
hdmiPortInfo[1] =
- new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+ new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfo);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index 4e8cf4a..5b1bdf6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -180,8 +180,11 @@
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR,
- true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 70f9e5c..c40cd0e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -102,9 +102,17 @@
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
hdmiPortInfos[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+ new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x1000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(false)
+ .build();
hdmiPortInfos[1] =
- new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
+ new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+ .setCecSupported(true)
+ .setMhlSupported(false)
+ .setArcSupported(true)
+ .build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index f2e03aa..e871fc5 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,7 +37,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -165,18 +164,6 @@
awaitJobStart(DEFAULT_WAIT_TIMEOUT));
}
- @FlakyTest
- @Test
- public void testFeatureFlag() throws Exception {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.FORCED_APP_STANDBY_ENABLED, 0);
- scheduleAndAssertJobStarted();
- setAppOpsModeAllowed(false);
- mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
- assertFalse("Job stopped even when feature flag was disabled",
- awaitJobStop(DEFAULT_WAIT_TIMEOUT, JobParameters.STOP_REASON_UNDEFINED));
- }
-
@After
public void tearDown() throws Exception {
final Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
@@ -187,8 +174,6 @@
Thread.sleep(500); // To avoid race with register in the next setUp
setAppOpsModeAllowed(true);
setPowerExemption(false);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.FORCED_APP_STANDBY_ENABLED, 1);
}
private void setPowerExemption(boolean exempt) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
index 8c3838b..23d7082 100644
--- a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
@@ -201,6 +201,32 @@
}
}
assertNull(jobQueue.next());
+ assertEquals(0, jobQueue.size());
+ }
+
+ @Test
+ public void testRemove_duringIteration() {
+ List<JobStatus> jobs = new ArrayList<>();
+ jobs.add(createJobStatus("testRemove", createJobInfo(1), 1));
+ jobs.add(createJobStatus("testRemove", createJobInfo(2), 2));
+ jobs.add(createJobStatus("testRemove", createJobInfo(3).setExpedited(true), 3));
+ jobs.add(createJobStatus("testRemove", createJobInfo(4), 4));
+ jobs.add(createJobStatus("testRemove", createJobInfo(5).setExpedited(true), 5));
+
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ jobQueue.addAll(jobs);
+
+ ArraySet<JobStatus> removed = new ArraySet<>();
+ JobStatus job;
+ jobQueue.resetIterator();
+ while ((job = jobQueue.next()) != null) {
+ jobQueue.remove(job);
+ removed.add(job);
+ assertFalse("Queue retained a removed job " + testJobToString(job),
+ jobQueue.contains(job));
+ }
+ assertNull(jobQueue.next());
+ assertEquals(0, jobQueue.size());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index ea5caa8..cc1100b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -55,6 +55,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index 19a606e..1cf4471 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -28,6 +28,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
+
import com.google.common.collect.ImmutableMap;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 1b983f0b..bb79fd8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -60,6 +60,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.security.SecureBox;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
index 2658af6..6f89ff0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -104,7 +104,7 @@
@Before
public void setUp() throws Exception {
Context context = InstrumentationRegistry.getTargetContext();
- mDatabaseHelper = new RecoverableKeyStoreDbHelper(context);
+ mDatabaseHelper = new RecoverableKeyStoreDbHelper(context, 7);
mDatabase = SQLiteDatabase.create(null);
}
@@ -128,7 +128,7 @@
@Test
public void onUpgrade_beforeV2() throws Exception {
mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1,
- RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+ RecoverableKeyStoreDbHelper.DATABASE_VERSION_7);
checkAllColumns_latest();
}
@@ -136,7 +136,7 @@
public void onUpgrade_fromV2() throws Exception {
createV2Tables();
mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2,
- RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+ RecoverableKeyStoreDbHelper.DATABASE_VERSION_7);
checkAllColumns_latest();
}
@@ -153,8 +153,7 @@
mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, /*newVersion=*/ 4);
checkAllColumns_v4();
- mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 4,
- RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+ mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 4, /*newVersion=*/ 7);
checkAllColumns_latest();
}
@@ -243,6 +242,14 @@
assertThat(
mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
.isGreaterThan(-1L);
+
+ // Bad guess counter was added when upgrading from v6 to v7
+ values = new ContentValues();
+ values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+ values.put(UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER, 2);
+ assertThat(
+ mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+ .isGreaterThan(-1L);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 7a20af4..e223a97 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -67,7 +67,7 @@
public void setUp() {
Context context = InstrumentationRegistry.getTargetContext();
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
- mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
+ mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context, 7);
}
@After
@@ -309,24 +309,63 @@
int userId = 42;
int generationId = 110;
Long serialNumber = 10L;
+ int badGuessCounter = 3;
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
+ mRecoverableKeyStoreDb.setBadRemoteGuessCounter(userId, badGuessCounter);
mRecoverableKeyStoreDb.setUserSerialNumber(userId, serialNumber);
+ assertEquals(badGuessCounter, mRecoverableKeyStoreDb.getBadRemoteGuessCounter(userId));
assertEquals(generationId, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
}
@Test
+ public void getRemoteBadGuessCounter_returnsZeroAsDefaultValue() {
+ assertEquals(0, mRecoverableKeyStoreDb.getBadRemoteGuessCounter(42));
+ }
+
+ @Test
+ public void getRemoteBadGuessCounter_returnsStoredValue() {
+ int userId = 42;
+ int userId2 = 44;
+ int badGuessCounter = 3;
+ int badGuessCounter2 = 4;
+
+ mRecoverableKeyStoreDb.setBadRemoteGuessCounter(userId, badGuessCounter);
+ mRecoverableKeyStoreDb.setBadRemoteGuessCounter(userId2, badGuessCounter2);
+
+ assertEquals(badGuessCounter, mRecoverableKeyStoreDb.getBadRemoteGuessCounter(userId));
+ assertEquals(badGuessCounter2, mRecoverableKeyStoreDb.getBadRemoteGuessCounter(userId2));
+ }
+
+ @Test
+ public void getBadRemoteGuessCounter_returnsStoredValue() {
+ int userId = 42;
+ int userId2 = 44;
+ int badGuessCounter = 3;
+ int badGuessCounter2 = 4;
+
+ mRecoverableKeyStoreDb.setBadRemoteGuessCounter(userId, badGuessCounter);
+ mRecoverableKeyStoreDb.setBadRemoteGuessCounter(userId2, badGuessCounter2);
+
+ assertEquals(badGuessCounter, mRecoverableKeyStoreDb.getBadRemoteGuessCounter(userId));
+ assertEquals(badGuessCounter2, mRecoverableKeyStoreDb.getBadRemoteGuessCounter(userId2));
+ }
+
+ @Test
public void setPlatformKeyGenerationId_keepsUserSerialNumber() {
int userId = 42;
int generationId = 110;
Long serialNumber = 10L;
+ int badGuessCounter = 3;
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
mRecoverableKeyStoreDb.setUserSerialNumber(userId, serialNumber);
- mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId + 1);
+ mRecoverableKeyStoreDb.setBadRemoteGuessCounter(userId, badGuessCounter);
assertEquals(serialNumber, mRecoverableKeyStoreDb.getUserSerialNumbers().get(userId));
+ assertEquals(generationId, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
+ assertEquals(badGuessCounter, mRecoverableKeyStoreDb.getBadRemoteGuessCounter(userId));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
new file mode 100644
index 0000000..05d30ed
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.locksettings.recoverablekeystore.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.SystemClock;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.security.SecureBox;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RemoteLockscreenValidationSessionStorageTest {
+ private static final int USER_ID = 0;
+ private static final int USER_ID_2 = 2;
+
+ RemoteLockscreenValidationSessionStorage mStorage;
+
+ @Before
+ public void setUp() {
+ mStorage = new RemoteLockscreenValidationSessionStorage();
+ }
+
+ @Test
+ public void get_noStoredSessions_returnsNull() {
+ assertThat(mStorage.get(USER_ID)).isNull();
+ }
+
+ @Test
+ public void startSession() {
+ mStorage.startSession(USER_ID);
+
+ assertThat(mStorage.get(USER_ID)).isNotNull();
+ assertThat(mStorage.get(USER_ID_2)).isNull();
+ }
+
+ @Test
+ public void finishSession_removesSessionFromStorage() {
+ mStorage.startSession(USER_ID);
+
+ mStorage.finishSession(USER_ID);
+
+ assertThat(mStorage.get(USER_ID)).isNull();
+ }
+
+ @Test
+ public void getLockscreenValidationCleanupTask() throws Exception {
+ long time11MinutesAgo = SystemClock.elapsedRealtime() - 11 * 60 * 1000;
+ long time2MinutesAgo = SystemClock.elapsedRealtime() - 2 * 60 * 1000;
+ mStorage.mSessionsByUserId.put(
+ USER_ID, mStorage.new LockscreenVerificationSession(
+ SecureBox.genKeyPair(), time11MinutesAgo));
+ mStorage.mSessionsByUserId.put(
+ USER_ID_2, mStorage.new LockscreenVerificationSession(
+ SecureBox.genKeyPair(), time2MinutesAgo));
+
+ mStorage.getLockscreenValidationCleanupTask().run();
+
+ assertThat(mStorage.get(USER_ID)).isNull();
+ assertThat(mStorage.get(USER_ID_2)).isNotNull();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index f568c76..2675f05 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -64,8 +64,9 @@
.setUseParentsContacts(false)
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(0)
- .setIsMediaSharedWithParent(false)
- .setIsCredentialSharableWithParent(true)
+ .setMediaSharedWithParent(false)
+ .setCredentialShareableWithParent(true)
+ .setDeleteAppWithParent(false)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
@@ -74,8 +75,9 @@
actualProps.setUseParentsContacts(true);
actualProps.setCrossProfileIntentFilterAccessControl(20);
actualProps.setCrossProfileIntentResolutionStrategy(1);
- actualProps.setIsMediaSharedWithParent(true);
- actualProps.setIsCredentialSharableWithParent(false);
+ actualProps.setMediaSharedWithParent(true);
+ actualProps.setCredentialShareableWithParent(false);
+ actualProps.setDeleteAppWithParent(true);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -115,13 +117,15 @@
.setStartWithParent(true)
.setShowInSettings(3452)
.setInheritDevicePolicy(1732)
- .setIsMediaSharedWithParent(true)
+ .setMediaSharedWithParent(true)
+ .setDeleteAppWithParent(true)
.build();
final UserProperties orig = new UserProperties(defaultProps);
orig.setShowInLauncher(2841);
orig.setStartWithParent(false);
orig.setShowInSettings(1437);
orig.setInheritDevicePolicy(9456);
+ orig.setDeleteAppWithParent(false);
// Test every permission level. (Currently, it's linear so it's easy.)
for (int permLevel = 0; permLevel < 4; permLevel++) {
@@ -163,6 +167,8 @@
copy::getCrossProfileIntentFilterAccessControl, exposeAll);
assertEqualGetterOrThrows(orig::getCrossProfileIntentResolutionStrategy,
copy::getCrossProfileIntentResolutionStrategy, exposeAll);
+ assertEqualGetterOrThrows(orig::getDeleteAppWithParent,
+ copy::getDeleteAppWithParent, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -174,10 +180,10 @@
// Items with no permission requirements.
assertEqualGetterOrThrows(orig::getShowInLauncher, copy::getShowInLauncher, true);
- assertEqualGetterOrThrows(orig::getIsMediaSharedWithParent,
- copy::getIsMediaSharedWithParent, true);
- assertEqualGetterOrThrows(orig::getIsCredentialSharableWithParent,
- copy::getIsCredentialSharableWithParent, true);
+ assertEqualGetterOrThrows(orig::isMediaSharedWithParent,
+ copy::isMediaSharedWithParent, true);
+ assertEqualGetterOrThrows(orig::isCredentialShareableWithParent,
+ copy::isCredentialShareableWithParent, true);
}
/**
@@ -223,9 +229,10 @@
.isEqualTo(actual.getCrossProfileIntentFilterAccessControl());
assertThat(expected.getCrossProfileIntentResolutionStrategy())
.isEqualTo(actual.getCrossProfileIntentResolutionStrategy());
- assertThat(expected.getIsMediaSharedWithParent())
- .isEqualTo(actual.getIsMediaSharedWithParent());
- assertThat(expected.getIsCredentialSharableWithParent())
- .isEqualTo(actual.getIsCredentialSharableWithParent());
+ assertThat(expected.isMediaSharedWithParent())
+ .isEqualTo(actual.isMediaSharedWithParent());
+ assertThat(expected.isCredentialShareableWithParent())
+ .isEqualTo(actual.isCredentialShareableWithParent());
+ assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index a2bbfba..ff9a79e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -87,8 +87,12 @@
.setUseParentsContacts(true)
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(1)
- .setIsMediaSharedWithParent(true)
- .setIsCredentialSharableWithParent(false);
+ .setMediaSharedWithParent(true)
+ .setCredentialShareableWithParent(false)
+ .setShowInSettings(900)
+ .setInheritDevicePolicy(340)
+ .setDeleteAppWithParent(true);
+
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(1)
@@ -150,8 +154,12 @@
.getCrossProfileIntentFilterAccessControl());
assertEquals(1, type.getDefaultUserPropertiesReference()
.getCrossProfileIntentResolutionStrategy());
- assertTrue(type.getDefaultUserPropertiesReference().getIsMediaSharedWithParent());
- assertFalse(type.getDefaultUserPropertiesReference().getIsCredentialSharableWithParent());
+ assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
+ assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
+ assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(340, type.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -200,8 +208,8 @@
assertEquals(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT, props.getShowInLauncher());
assertEquals(UserProperties.CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT,
props.getCrossProfileIntentResolutionStrategy());
- assertFalse(props.getIsMediaSharedWithParent());
- assertFalse(props.getIsCredentialSharableWithParent());
+ assertFalse(props.isMediaSharedWithParent());
+ assertFalse(props.isCredentialShareableWithParent());
assertFalse(type.hasBadge());
}
@@ -286,8 +294,12 @@
.setUseParentsContacts(true)
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(1)
- .setIsMediaSharedWithParent(false)
- .setIsCredentialSharableWithParent(true);
+ .setMediaSharedWithParent(false)
+ .setCredentialShareableWithParent(true)
+ .setShowInSettings(20)
+ .setInheritDevicePolicy(21)
+ .setDeleteAppWithParent(true);
+
final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
builders.put(userTypeAosp1, new UserTypeDetails.Builder()
.setName(userTypeAosp1)
@@ -320,9 +332,13 @@
assertTrue(aospType.getDefaultUserPropertiesReference().getStartWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
.getUseParentsContacts());
- assertFalse(aospType.getDefaultUserPropertiesReference().getIsMediaSharedWithParent());
+ assertFalse(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
- .getIsCredentialSharableWithParent());
+ .isCredentialShareableWithParent());
+ assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(21, aospType.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
// userTypeAosp2 should be modified.
aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -359,9 +375,14 @@
assertFalse(aospType.getDefaultUserPropertiesReference().getStartWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
.getUseParentsContacts());
- assertTrue(aospType.getDefaultUserPropertiesReference().getIsMediaSharedWithParent());
+ assertTrue(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
- .getIsCredentialSharableWithParent());
+ .isCredentialShareableWithParent());
+ assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(450, aospType.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertFalse(aospType.getDefaultUserPropertiesReference()
+ .getDeleteAppWithParent());
// userTypeOem1 should be created.
UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index f2bc654..c760efd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -144,7 +144,9 @@
@Test
public void testCloneUser() throws Exception {
-
+ assumeCloneEnabled();
+ UserHandle mainUser = mUserManager.getMainUser();
+ assumeTrue("Main user is null", mainUser != null);
// Get the default properties for clone user type.
final UserTypeDetails userTypeDetails =
UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_PROFILE_CLONE);
@@ -153,7 +155,7 @@
final UserProperties typeProps = userTypeDetails.getDefaultUserPropertiesReference();
// Test that only one clone user can be created
- final int mainUserId = mUserManager.getMainUser().getIdentifier();
+ final int mainUserId = mainUser.getIdentifier();
UserInfo userInfo = createProfileForUser("Clone user1",
UserManager.USER_TYPE_PROFILE_CLONE,
mainUserId);
@@ -188,10 +190,11 @@
cloneUserProperties::getCrossProfileIntentFilterAccessControl);
assertThrows(SecurityException.class,
cloneUserProperties::getCrossProfileIntentResolutionStrategy);
- assertThat(typeProps.getIsMediaSharedWithParent())
- .isEqualTo(cloneUserProperties.getIsMediaSharedWithParent());
- assertThat(typeProps.getIsCredentialSharableWithParent())
- .isEqualTo(cloneUserProperties.getIsCredentialSharableWithParent());
+ assertThat(typeProps.isMediaSharedWithParent())
+ .isEqualTo(cloneUserProperties.isMediaSharedWithParent());
+ assertThat(typeProps.isCredentialShareableWithParent())
+ .isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
+ assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
// Verify clone user parent
assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
@@ -535,6 +538,7 @@
@Test
public void testRemoveUserWhenPossible_withProfiles() throws Exception {
assumeHeadlessModeEnabled();
+ assumeCloneEnabled();
final UserInfo parentUser = createUser("Human User", /* flags= */ 0);
final UserInfo cloneProfileUser = createProfileForUser("Clone Profile user",
UserManager.USER_TYPE_PROFILE_CLONE,
@@ -842,10 +846,12 @@
assertThrows(SecurityException.class, userProps::getCrossProfileIntentResolutionStrategy);
assertThrows(SecurityException.class, userProps::getStartWithParent);
assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
- assertThat(userProps.getIsMediaSharedWithParent()).isFalse();
- assertThat(userProps.getIsCredentialSharableWithParent()).isTrue();
+ assertThat(userProps.isMediaSharedWithParent()).isFalse();
+ assertThat(userProps.isCredentialShareableWithParent()).isTrue();
+ assertThrows(SecurityException.class, userProps::getDeleteAppWithParent);
}
+
// Make sure only max managed profiles can be created
@MediumTest
@Test
@@ -1536,6 +1542,12 @@
UserManager.isHeadlessSystemUserMode());
}
+ private void assumeCloneEnabled() {
+ // assume clone profile is supported on the device
+ assumeTrue("Device doesn't support clone profiles ",
+ mUserManager.isUserTypeEnabled(UserManager.USER_TYPE_PROFILE_CLONE));
+ }
+
private boolean isAutomotive() {
return mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
index 71c8c1d..4fde73b 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
@@ -33,21 +33,27 @@
import java.io.File;
import java.util.Random;
+import java.util.concurrent.Future;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@SuppressWarnings("GuardedBy")
public class BatteryStatsHistoryIteratorTest {
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
- private MockClock mMockClock = new MockClock();
+ private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStats;
- private Random mRandom = new Random();
+ private final Random mRandom = new Random();
+ private final MockExternalStatsSync mExternalStatsSync = new MockExternalStatsSync();
@Before
public void setup() {
- final File historyDir =
- createTemporaryDirectory(getClass().getSimpleName());
+ final File historyDir = createTemporaryDirectory(getClass().getSimpleName());
mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
+ mBatteryStats.setDummyExternalStatsSync(mExternalStatsSync);
+ mBatteryStats.setRecordAllHistoryLocked(true);
+ mBatteryStats.forceRecordAllHistory();
+ mBatteryStats.setNoAutoReset(true);
}
/**
@@ -65,42 +71,24 @@
@Test
public void testIterator() {
- synchronized (mBatteryStats) {
- mBatteryStats.setRecordAllHistoryLocked(true);
- }
- mBatteryStats.forceRecordAllHistory();
-
mMockClock.realtime = 1000;
mMockClock.uptime = 1000;
- mBatteryStats.setNoAutoReset(true);
- synchronized (mBatteryStats) {
- mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
- 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
- 1_000_000, 1_000_000);
- }
- synchronized (mBatteryStats) {
- mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
- 100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
- 2_000_000, 2_000_000);
- }
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
+ 1_000_000, 1_000_000);
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
+ 2_000_000, 2_000_000);
+ mBatteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000);
+ mBatteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000);
- synchronized (mBatteryStats) {
- mBatteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000);
- }
- synchronized (mBatteryStats) {
- mBatteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000);
- }
-
- final BatteryStatsHistoryIterator iterator =
- mBatteryStats.iterateBatteryStatsHistory();
+ final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory();
BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
- assertHistoryItem(item,
- BatteryStats.HistoryItem.CMD_RESET, BatteryStats.HistoryItem.EVENT_NONE,
- null, 0, 3_600_000, 90, 1_000_000);
+ assertThat(item.cmd).isEqualTo(BatteryStats.HistoryItem.CMD_RESET);
assertThat(item = iterator.next()).isNotNull();
assertHistoryItem(item,
@@ -114,11 +102,6 @@
assertThat(item = iterator.next()).isNotNull();
assertHistoryItem(item,
- BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
- null, 0, 2_400_000, 80, 2_000_000);
-
- assertThat(item = iterator.next()).isNotNull();
- assertHistoryItem(item,
BatteryStats.HistoryItem.CMD_UPDATE,
BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START,
"foo", APP_UID, 2_400_000, 80, 3_000_000);
@@ -136,35 +119,20 @@
// Test history that spans multiple buffers and uses more than 32k different strings.
@Test
public void tagsLongHistory() {
- synchronized (mBatteryStats) {
- mBatteryStats.setRecordAllHistoryLocked(true);
- }
- mBatteryStats.forceRecordAllHistory();
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
+ 1_000_000, 1_000_000);
- mMockClock.realtime = 1000;
- mMockClock.uptime = 1000;
- mBatteryStats.setNoAutoReset(true);
-
- synchronized (mBatteryStats) {
- mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
- 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
- 1_000_000, 1_000_000);
- }
// More than 32k strings
final int eventCount = 0x7FFF + 100;
for (int i = 0; i < eventCount; i++) {
// Names repeat in order to verify de-duping of identical history tags.
String name = "a" + (i % 10);
- synchronized (mBatteryStats) {
- mBatteryStats.noteAlarmStartLocked(name, null, APP_UID, 3_000_000, 2_000_000);
- }
- synchronized (mBatteryStats) {
- mBatteryStats.noteAlarmFinishLocked(name, null, APP_UID, 3_500_000, 2_500_000);
- }
+ mBatteryStats.noteAlarmStartLocked(name, null, APP_UID, 3_000_000, 2_000_000);
+ mBatteryStats.noteAlarmFinishLocked(name, null, APP_UID, 3_500_000, 2_500_000);
}
- final BatteryStatsHistoryIterator iterator =
- mBatteryStats.iterateBatteryStatsHistory();
+ final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory();
BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
@@ -210,6 +178,79 @@
assertThat(iterator.next()).isNull();
}
+ @Test
+ public void cpuSuspendHistoryEvents() {
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
+ 1_000_000, 1_000_000, 1_000_000);
+
+ mExternalStatsSync.updateCpuStats(100, 1_100_000, 1_100_000);
+
+ // Device was suspended for 3_000 seconds, note the difference in elapsed time and uptime
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
+ 5_000_000, 2_000_000, 5_000_000);
+
+ mExternalStatsSync.updateCpuStats(200, 5_100_000, 2_100_000);
+
+ // Battery level is unchanged, so we don't write battery level details in history
+ mBatteryStats.noteAlarmStartLocked("wakeup", null, APP_UID, 6_000_000, 3_000_000);
+
+ assertThat(mExternalStatsSync.isSyncScheduled()).isFalse();
+
+ // Battery level drops, so we write the accumulated battery level details
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 79, 72, 3700, 2_000_000, 4_000_000, 0,
+ 7_000_000, 4_000_000, 6_000_000);
+
+ mExternalStatsSync.updateCpuStats(300, 7_100_000, 4_100_000);
+
+ final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory();
+
+ BatteryStats.HistoryItem item;
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.cmd).isEqualTo((int) BatteryStats.HistoryItem.CMD_RESET);
+ assertThat(item.stepDetails).isNull();
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(90);
+ assertThat(item.stepDetails).isNull();
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(90);
+ assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(90);
+ assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isEqualTo(0);
+ assertThat(item.stepDetails.userTime).isEqualTo(100);
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(80);
+ assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
+ assertThat(item.stepDetails.userTime).isEqualTo(0);
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(80);
+ assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(80);
+ assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM_START);
+ assertThat(item.stepDetails).isNull();
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(79);
+ assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
+ assertThat(item.stepDetails.userTime).isEqualTo(200);
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(79);
+ assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+
+ assertThat(item = iterator.next()).isNull();
+ }
+
private void assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode,
String tag, int uid, int batteryChargeUah, int batteryLevel,
long elapsedTimeMs) {
@@ -226,4 +267,27 @@
assertThat(item.time).isEqualTo(elapsedTimeMs);
}
+
+ private class MockExternalStatsSync extends MockBatteryStatsImpl.DummyExternalStatsSync {
+ private boolean mSyncScheduled;
+
+ @Override
+ public Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis) {
+ mSyncScheduled = true;
+ return null;
+ }
+
+ public boolean isSyncScheduled() {
+ return mSyncScheduled;
+ }
+
+ public void updateCpuStats(int totalUTimeMs, long elapsedRealtime, long uptime) {
+ assertThat(mExternalStatsSync.mSyncScheduled).isTrue();
+ mBatteryStats.recordHistoryEventLocked(elapsedRealtime, uptime,
+ BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, "wakelock-update", 0);
+ mBatteryStats.addCpuStatsLocked(totalUTimeMs, 0, 0, 0, 0, 0, 0, 0);
+ mBatteryStats.finishAddingCpuStatsLocked();
+ mExternalStatsSync.mSyncScheduled = false;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 998d22e..6b21eb0 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -937,15 +937,14 @@
HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
+ assertEquals(HistoryItem.CMD_RESET, item.cmd);
+ assertEquals(HistoryItem.EVENT_NONE, item.eventCode);
+
+ assertThat(item = iterator.next()).isNotNull();
assertEquals(HistoryItem.EVENT_ALARM_START, item.eventCode);
assertEquals("foo", item.eventTag.string);
assertEquals(UID, item.eventTag.uid);
- // TODO(narayan): Figure out why this event is written to the history buffer. See
- // test below where it is being interspersed between multiple START events too.
- assertThat(item = iterator.next()).isNotNull();
- assertEquals(HistoryItem.EVENT_NONE, item.eventCode);
-
assertThat(item = iterator.next()).isNotNull();
assertEquals(HistoryItem.EVENT_ALARM_FINISH, item.eventCode);
assertTrue(item.isDeltaData());
@@ -977,14 +976,15 @@
HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
+ assertEquals(HistoryItem.CMD_RESET, item.cmd);
+ assertEquals(HistoryItem.EVENT_NONE, item.eventCode);
+
+ assertThat(item = iterator.next()).isNotNull();
assertEquals(HistoryItem.EVENT_ALARM_START, item.eventCode);
assertEquals("foo", item.eventTag.string);
assertEquals(100, item.eventTag.uid);
assertThat(item = iterator.next()).isNotNull();
- assertEquals(HistoryItem.EVENT_NONE, item.eventCode);
-
- assertThat(item = iterator.next()).isNotNull();
assertEquals(HistoryItem.EVENT_ALARM_START, item.eventCode);
assertEquals("foo", item.eventTag.string);
assertEquals(500, item.eventTag.uid);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 968609b..2f64506 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -230,11 +230,13 @@
assertHistoryItem(item,
BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
null, 0, 3_600_000, 90, 1_000_000);
+ assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
assertThat(item = iterator.next()).isNotNull();
assertHistoryItem(item,
BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE,
null, 0, 3_600_000, 90, 2_000_000);
+ assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isEqualTo(0);
assertThat(item = iterator.next()).isNotNull();
assertHistoryItem(item,
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index 5234bb7..63d6768 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -46,7 +46,7 @@
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
-import android.hardware.boot.V1_2.IBootControl;
+import android.hardware.boot.IBootControl;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IRecoverySystemProgressListener;
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
index 27e953f..f1cf48b 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
@@ -17,7 +17,7 @@
package com.android.server.recoverysystem;
import android.content.Context;
-import android.hardware.boot.V1_2.IBootControl;
+import android.hardware.boot.IBootControl;
import android.os.PowerManager;
import com.android.internal.widget.LockSettingsInternal;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
index 50040b7..704b06b 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
@@ -48,17 +48,17 @@
}
@Override
- public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) {
+ public void suggestTelephonyTime(TelephonyTimeSuggestion suggestion) {
}
@Override
- public boolean suggestManualTime(@UserIdInt int userId, ManualTimeSuggestion timeSuggestion,
+ public boolean suggestManualTime(@UserIdInt int userId, ManualTimeSuggestion suggestion,
boolean bypassUserPolicyChecks) {
return true;
}
@Override
- public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
+ public void suggestNetworkTime(NetworkTimeSuggestion suggestion) {
}
@Override
@@ -71,11 +71,11 @@
}
@Override
- public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
+ public void suggestGnssTime(GnssTimeSuggestion suggestion) {
}
@Override
- public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) {
+ public void suggestExternalTime(ExternalTimeSuggestion suggestion) {
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 1c014d1..590aba9 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -67,18 +67,17 @@
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
-import android.os.HandlerThread;
import android.service.timezone.TimeZoneProviderStatus;
import com.android.server.SystemTimeZone.TimeZoneConfidence;
import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -208,8 +207,6 @@
private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy;
private FakeEnvironment mFakeEnvironment;
- private HandlerThread mHandlerThread;
- private TestHandler mTestHandler;
private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
@@ -220,18 +217,8 @@
mFakeServiceConfigAccessorSpy.initializeCurrentUserConfiguration(
CONFIG_AUTO_DISABLED_GEO_DISABLED);
- // Create a thread + handler for processing the work that the strategy posts.
- mHandlerThread = new HandlerThread("TimeZoneDetectorStrategyImplTest");
- mHandlerThread.start();
- mTestHandler = new TestHandler(mHandlerThread.getLooper());
mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(
- mFakeServiceConfigAccessorSpy, mTestHandler, mFakeEnvironment);
- }
-
- @After
- public void tearDown() throws Exception {
- mHandlerThread.quit();
- mHandlerThread.join();
+ mFakeServiceConfigAccessorSpy, mFakeEnvironment);
}
@Test
@@ -1723,6 +1710,7 @@
private final TestState<String> mTimeZoneId = new TestState<>();
private final TestState<Integer> mTimeZoneConfidence = new TestState<>();
+ private final List<Runnable> mAsyncRunnables = new ArrayList<>();
private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
FakeEnvironment() {
@@ -1795,12 +1783,24 @@
public void dumpDebugLog(PrintWriter printWriter) {
// No-op for tests
}
+
+ @Override
+ public void runAsync(Runnable runnable) {
+ mAsyncRunnables.add(runnable);
+ }
+
+ public void runAsyncRunnables() {
+ for (Runnable runnable : mAsyncRunnables) {
+ runnable.run();
+ }
+ mAsyncRunnables.clear();
+ }
}
private void assertStateChangeNotificationsSent(
TestStateChangeListener stateChangeListener, int expectedCount) {
- // State change notifications are asynchronous, so we have to wait.
- mTestHandler.waitForMessagesToBeProcessed();
+ // The fake environment needs to be told to run posted work.
+ mFakeEnvironment.runAsyncRunnables();
stateChangeListener.assertNotificationsReceivedAndReset(expectedCount);
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index dcf1b35..9570ff6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -87,6 +87,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.hardware.display.DisplayManager;
+import android.os.BatteryStats;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -119,6 +120,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -249,6 +251,8 @@
.setLong("elapsed_threshold_rare", RARE_THRESHOLD)
.setLong("elapsed_threshold_restricted", RESTRICTED_THRESHOLD);
DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
+ String mExpectedNoteEventPackage = null;
+ int mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
MyInjector(Context context, Looper looper) {
super(context, looper);
@@ -320,6 +324,9 @@
@Override
void noteEvent(int event, String packageName, int uid) throws RemoteException {
+ if (Objects.equals(mExpectedNoteEventPackage, packageName)) {
+ mLastNoteEvent = event;
+ }
}
@Override
@@ -2103,6 +2110,50 @@
assertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_BACKGROUND_LOCATION);
}
+ @Test
+ public void testBatteryStatsNoteEvent() throws Exception {
+ mInjector.mExpectedNoteEventPackage = PACKAGE_1;
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+
+ // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
+ // Reset the last event to confirm the method isn't called.
+ mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+ // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
+ // Reset the last event to confirm the method isn't called.
+ mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_EXEMPTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 582e744..973d5d0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -1582,6 +1582,55 @@
}
@Test
+ public void testSetComponentState_differentUsers() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mContext.getPackageName());
+ when(context.getUserId()).thenReturn(mContext.getUserId());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ addExpectedServices(service, Arrays.asList("a"), mZero.id);
+ addExpectedServices(service, Arrays.asList("a"), mTen.id);
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onServiceConnected(cn, mock(IBinder.class));
+ return true;
+ });
+ service.addApprovedList("a/a", 0, true);
+ service.addApprovedList("a/a", 10, false);
+
+ service.registerService(cn, mZero.id);
+ assertTrue(service.isBound(cn, mZero.id));
+
+ service.onUserSwitched(mTen.id);
+ assertFalse(service.isBound(cn, mZero.id));
+ service.registerService(cn, mTen.id);
+ assertTrue(service.isBound(cn, mTen.id));
+
+ service.setComponentState(cn, mTen.id, false);
+ assertFalse(service.isBound(cn, mZero.id));
+ assertFalse(service.isBound(cn, mTen.id));
+
+ // Service should be rebound on user 0, since it was only disabled for user 10.
+ service.onUserSwitched(mZero.id);
+ assertTrue(service.isBound(cn, mZero.id));
+ assertFalse(service.isBound(cn, mTen.id));
+
+ // Service should stay unbound on going back to user 10.
+ service.onUserSwitched(mTen.id);
+ assertFalse(service.isBound(cn, mZero.id));
+ assertFalse(service.isBound(cn, mTen.id));
+ }
+ @Test
public void testOnPackagesChanged_nullValuesPassed_noNullPointers() {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1847,7 +1896,7 @@
}
private void addExpectedServices(final ManagedServices service, final List<String> packages,
- int userId) {
+ int userId) throws Exception {
ManagedServices.Config config = service.getConfig();
when(mPm.queryIntentServicesAsUser(any(), anyInt(), eq(userId))).
thenAnswer(new Answer<List<ResolveInfo>>() {
@@ -1876,6 +1925,20 @@
return new ArrayList<>();
}
});
+
+ when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
+ (Answer<ServiceInfo>) invocation -> {
+ ComponentName invocationCn = invocation.getArgument(0);
+ if (invocationCn != null && packages.contains(invocationCn.getPackageName())) {
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = invocationCn.getPackageName();
+ serviceInfo.name = invocationCn.getClassName();
+ serviceInfo.permission = service.getConfig().bindPermission;
+ return serviceInfo;
+ }
+ return null;
+ }
+ );
}
private List<String> stringToList(String list) {
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/OWNERS
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e663245..9fda204 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1475,7 +1475,6 @@
// Make keyguard locked and set the top activity show-when-locked.
KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
int displayId = activity.getDisplayId();
- doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId));
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.setVisibleRequested(true);
topActivity.nowVisible = true;
@@ -1485,18 +1484,24 @@
anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
topActivity.setShowWhenLocked(true);
- // Verify the stack-top activity is occluded keyguard.
- assertEquals(topActivity, task.topRunningActivity());
- assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ try {
+ keyguardController.setKeyguardShown(displayId, true, false);
- // Finish the top activity
- topActivity.setState(PAUSED, "true");
- topActivity.finishing = true;
- topActivity.completeFinishing("test");
+ // Verify the stack-top activity is occluded keyguard.
+ assertEquals(topActivity, task.topRunningActivity());
+ assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
- // Verify new top activity does not occlude keyguard.
- assertEquals(activity, task.topRunningActivity());
- assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ // Finish the top activity
+ topActivity.setState(PAUSED, "true");
+ topActivity.finishing = true;
+ topActivity.completeFinishing("test");
+
+ // Verify new top activity does not occlude keyguard.
+ assertEquals(activity, task.topRunningActivity());
+ assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ } finally {
+ keyguardController.setKeyguardShown(displayId, false, false);
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 1deb58e..eabbc76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -99,6 +99,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
import android.service.voice.IVoiceInteractionSession;
import android.util.Pair;
import android.util.Size;
@@ -108,11 +109,14 @@
import androidx.test.filters.SmallTest;
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
import com.android.server.wm.utils.MockTracker;
+import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -130,10 +134,9 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityStarterTests extends WindowTestsBase {
- private ActivityStartController mController;
- private ActivityMetricsLogger mActivityMetricsLogger;
- private PackageManagerInternal mMockPackageManager;
+ private static final String ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER =
+ "enable_default_rescind_bal_privileges_from_pending_intent_sender";
private static final int PRECONDITION_NO_CALLER_APP = 1;
private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
@@ -144,7 +147,6 @@
private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
-
private static final int FAKE_CALLING_UID = 666;
private static final int FAKE_REAL_CALLING_UID = 667;
private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
@@ -152,6 +154,13 @@
private static final int UNIMPORTANT_UID2 = 12346;
private static final int CURRENT_IME_UID = 12347;
+ protected final DeviceConfigStateHelper mDeviceConfig = new DeviceConfigStateHelper(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER);
+
+ private ActivityStartController mController;
+ private ActivityMetricsLogger mActivityMetricsLogger;
+ private PackageManagerInternal mMockPackageManager;
+
@Before
public void setUp() throws Exception {
mController = mock(ActivityStartController.class);
@@ -160,6 +169,13 @@
doReturn(balController).when(mController).getBackgroundActivityLaunchController();
mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
clearInvocations(mActivityMetricsLogger);
+ mDeviceConfig.set(ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER,
+ String.valueOf(true));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mDeviceConfig.close();
}
@Test
@@ -754,6 +770,7 @@
* start the background activity.
*/
@Test
+ @Ignore("b/266015587")
public void testBackgroundActivityStartsDisallowed_realCallingUidHasVisibleWindowAborted() {
doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 3dcae91..305260b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -252,7 +252,12 @@
mAtm.setLockScreenShown(true, true);
mRootWindowContainer.forAllDisplays(displayContent -> {
assertTrue(displayContent.isKeyguardLocked());
- assertTrue(displayContent.isAodShowing());
+ // Only default display supports AOD.
+ if (displayContent.isDefaultDisplay) {
+ assertTrue(displayContent.isAodShowing());
+ } else {
+ assertFalse(displayContent.isAodShowing());
+ }
});
// Check setLockScreenShown unlocking both displays
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
index 86732c9..2a28ae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
@@ -16,13 +16,12 @@
package com.android.server.wm;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
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.mockito.ArgumentMatchers.any;
import android.content.Context;
import android.content.res.Resources;
@@ -32,9 +31,10 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
import java.util.function.Consumer;
@@ -48,92 +48,76 @@
@Presubmit
public class DeviceStateControllerTests {
- private DeviceStateController.FoldStateListener mFoldStateListener;
private DeviceStateController mTarget;
private DeviceStateControllerBuilder mBuilder;
private Context mMockContext;
- private Handler mMockHandler;
- private Resources mMockRes;
private DeviceStateManager mMockDeviceStateManager;
-
- private Consumer<DeviceStateController.FoldState> mDelegate;
- private DeviceStateController.FoldState mCurrentState = DeviceStateController.FoldState.UNKNOWN;
+ private DeviceStateController.DeviceState mCurrentState =
+ DeviceStateController.DeviceState.UNKNOWN;
@Before
public void setUp() {
mBuilder = new DeviceStateControllerBuilder();
- mCurrentState = DeviceStateController.FoldState.UNKNOWN;
+ mCurrentState = DeviceStateController.DeviceState.UNKNOWN;
}
- private void initialize(boolean supportFold, boolean supportHalfFold) throws Exception {
+ private void initialize(boolean supportFold, boolean supportHalfFold) {
mBuilder.setSupportFold(supportFold, supportHalfFold);
- mDelegate = (newFoldState) -> {
+ Consumer<DeviceStateController.DeviceState> delegate = (newFoldState) -> {
mCurrentState = newFoldState;
};
- mBuilder.setDelegate(mDelegate);
+ mBuilder.setDelegate(delegate);
mBuilder.build();
- verifyFoldStateListenerRegistration(1);
+ verify(mMockDeviceStateManager).registerCallback(any(), any());
}
@Test
- public void testInitialization() throws Exception {
+ public void testInitialization() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
- mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
- assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+ mTarget.onStateChanged(mOpenDeviceStates[0]);
+ assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
}
@Test
- public void testInitializationWithNoFoldSupport() throws Exception {
+ public void testInitializationWithNoFoldSupport() {
initialize(false /* supportFold */, false /* supportHalfFolded */);
- mFoldStateListener.onStateChanged(mFoldedStates[0]);
+ mTarget.onStateChanged(mFoldedStates[0]);
// Note that the folded state is ignored.
- assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+ assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);
}
@Test
- public void testWithFoldSupported() throws Exception {
+ public void testWithFoldSupported() {
initialize(true /* supportFold */, false /* supportHalfFolded */);
- mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
- assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
- mFoldStateListener.onStateChanged(mFoldedStates[0]);
- assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
- mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
- assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); // Ignored
+ mTarget.onStateChanged(mOpenDeviceStates[0]);
+ assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ mTarget.onStateChanged(mFoldedStates[0]);
+ assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ mTarget.onStateChanged(mHalfFoldedStates[0]);
+ assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
}
@Test
- public void testWithHalfFoldSupported() throws Exception {
+ public void testWithHalfFoldSupported() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
- mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
- assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
- mFoldStateListener.onStateChanged(mFoldedStates[0]);
- assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
- mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
- assertEquals(mCurrentState, DeviceStateController.FoldState.HALF_FOLDED);
+ mTarget.onStateChanged(mOpenDeviceStates[0]);
+ assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ mTarget.onStateChanged(mFoldedStates[0]);
+ assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ mTarget.onStateChanged(mHalfFoldedStates[0]);
+ assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
}
-
private final int[] mFoldedStates = {0};
- private final int[] mUnfoldedStates = {1};
+ private final int[] mOpenDeviceStates = {1};
private final int[] mHalfFoldedStates = {2};
-
-
- private void verifyFoldStateListenerRegistration(int numOfInvocation) {
- final ArgumentCaptor<DeviceStateController.FoldStateListener> listenerCaptor =
- ArgumentCaptor.forClass(DeviceStateController.FoldStateListener.class);
- verify(mMockDeviceStateManager, times(numOfInvocation)).registerCallback(
- any(),
- listenerCaptor.capture());
- if (numOfInvocation > 0) {
- mFoldStateListener = listenerCaptor.getValue();
- }
- }
+ private final int[] mRearDisplayStates = {3};
private class DeviceStateControllerBuilder {
private boolean mSupportFold = false;
private boolean mSupportHalfFold = false;
- private Consumer<DeviceStateController.FoldState> mDelegate;
+ private Consumer<DeviceStateController.DeviceState> mDelegate;
DeviceStateControllerBuilder setSupportFold(
boolean supportFold, boolean supportHalfFold) {
@@ -143,34 +127,44 @@
}
DeviceStateControllerBuilder setDelegate(
- Consumer<DeviceStateController.FoldState> delegate) {
+ Consumer<DeviceStateController.DeviceState> delegate) {
mDelegate = delegate;
return this;
}
private void mockFold(boolean enableFold, boolean enableHalfFold) {
+ if (enableFold || enableHalfFold) {
+ when(mMockContext.getResources()
+ .getIntArray(R.array.config_openDeviceStates))
+ .thenReturn(mOpenDeviceStates);
+ when(mMockContext.getResources()
+ .getIntArray(R.array.config_rearDisplayDeviceStates))
+ .thenReturn(mRearDisplayStates);
+ }
+
if (enableFold) {
- when(mMockContext.getResources().getIntArray(
- com.android.internal.R.array.config_foldedDeviceStates))
+ when(mMockContext.getResources()
+ .getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(mFoldedStates);
}
if (enableHalfFold) {
- when(mMockContext.getResources().getIntArray(
- com.android.internal.R.array.config_halfFoldedDeviceStates))
+ when(mMockContext.getResources()
+ .getIntArray(R.array.config_halfFoldedDeviceStates))
.thenReturn(mHalfFoldedStates);
}
}
- private void build() throws Exception {
+ private void build() {
mMockContext = mock(Context.class);
- mMockRes = mock(Resources.class);
- when(mMockContext.getResources()).thenReturn((mMockRes));
mMockDeviceStateManager = mock(DeviceStateManager.class);
when(mMockContext.getSystemService(DeviceStateManager.class))
.thenReturn(mMockDeviceStateManager);
+ Resources mockRes = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn((mockRes));
mockFold(mSupportFold, mSupportHalfFold);
- mMockHandler = mock(Handler.class);
- mTarget = new DeviceStateController(mMockContext, mMockHandler, mDelegate);
+ Handler mockHandler = mock(Handler.class);
+ mTarget = new DeviceStateController(mMockContext, mockHandler);
+ mTarget.registerDeviceStateCallback(mDelegate);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 78707d6..3b34ba4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1738,7 +1738,7 @@
// No need to apply rotation if the display ignores orientation request.
doCallRealMethod().when(displayContent).rotationForActivityInDifferentOrientation(any());
- pinnedActivity.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+ pinnedActivity.setOverrideOrientation(SCREEN_ORIENTATION_LANDSCAPE);
displayContent.setIgnoreOrientationRequest(true);
assertEquals(WindowConfiguration.ROTATION_UNDEFINED,
displayContent.rotationForActivityInDifferentOrientation(pinnedActivity));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 4ce43e1..ed2b0a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -56,6 +56,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -111,6 +112,7 @@
private ContentResolver mMockResolver;
private FakeSettingsProvider mFakeSettingsProvider;
private StatusBarManagerInternal mMockStatusBarManagerInternal;
+ private DeviceStateManager mMockDeviceStateManager;
// Fields below are callbacks captured from test target.
private ContentObserver mShowRotationSuggestionsObserver;
@@ -120,6 +122,7 @@
private DisplayRotationBuilder mBuilder;
+ private DeviceStateController mDeviceStateController;
private DisplayRotation mTarget;
@BeforeClass
@@ -484,6 +487,34 @@
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
+ @Test
+ public void testReverseRotation() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(true);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_270));
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_180));
+ }
+
private boolean waitForUiHandler() {
final CountDownLatch latch = new CountDownLatch(1);
UiThread.getHandler().post(latch::countDown);
@@ -705,7 +736,7 @@
enableOrientationSensor();
- mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
+ mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
freezeRotation(Surface.ROTATION_270);
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
@@ -715,7 +746,7 @@
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
// ... until half-fold
- mTarget.foldStateChanged(DeviceStateController.FoldState.HALF_FOLDED);
+ mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
assertTrue(waitForUiHandler());
verify(sMockWm).updateRotation(false, false);
assertTrue(waitForUiHandler());
@@ -723,7 +754,7 @@
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
// ... then transition back to flat
- mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
+ mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
assertTrue(waitForUiHandler());
verify(sMockWm, atLeast(1)).updateRotation(false, false);
assertTrue(waitForUiHandler());
@@ -1097,8 +1128,14 @@
mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
+ mMockDeviceStateManager = mock(DeviceStateManager.class);
+ when(mMockContext.getSystemService(eq(DeviceStateManager.class)))
+ .thenReturn(mMockDeviceStateManager);
+
+ mDeviceStateController = mock(DeviceStateController.class);
mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayAddress,
- mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object()) {
+ mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object(),
+ mDeviceStateController) {
@Override
DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
WindowManagerService service, DisplayContent displayContent) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 5e087f0..110ef89 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -16,33 +16,61 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
+import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
+import android.annotation.Nullable;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
+import android.content.res.Resources;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
+import android.view.RoundedCorners;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
+
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import org.junit.Before;
@@ -61,14 +89,24 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class LetterboxUiControllerTest extends WindowTestsBase {
+ private static final int TASKBAR_COLLAPSED_HEIGHT = 10;
+ private static final int TASKBAR_EXPANDED_HEIGHT = 20;
+ private static final int SCREEN_WIDTH = 200;
+ private static final int SCREEN_HEIGHT = 100;
+ private static final Rect TASKBAR_COLLAPSED_BOUNDS = new Rect(0,
+ SCREEN_HEIGHT - TASKBAR_COLLAPSED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
+ private static final Rect TASKBAR_EXPANDED_BOUNDS = new Rect(0,
+ SCREEN_HEIGHT - TASKBAR_EXPANDED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
private ActivityRecord mActivity;
+ private Task mTask;
private DisplayContent mDisplayContent;
private LetterboxUiController mController;
private LetterboxConfiguration mLetterboxConfiguration;
+ private final Rect mLetterboxedPortraitTaskBounds = new Rect();
@Before
public void setUp() throws Exception {
@@ -308,6 +346,289 @@
assertTrue(mController.shouldForceRotateForCameraCompat());
}
+ @Test
+ public void testGetCropBoundsIfNeeded_noCrop() {
+ final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
+ InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
+
+ // Do not apply crop if taskbar is collapsed
+ taskbar.setFrame(TASKBAR_COLLAPSED_BOUNDS);
+ assertNull(mController.getExpandedTaskbarOrNull(mainWindow));
+
+ mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4,
+ SCREEN_WIDTH - SCREEN_WIDTH / 4, SCREEN_HEIGHT - SCREEN_HEIGHT / 4);
+
+ final Rect noCrop = mController.getCropBoundsIfNeeded(mainWindow);
+ assertNotEquals(null, noCrop);
+ assertEquals(0, noCrop.left);
+ assertEquals(0, noCrop.top);
+ assertEquals(mLetterboxedPortraitTaskBounds.width(), noCrop.right);
+ assertEquals(mLetterboxedPortraitTaskBounds.height(), noCrop.bottom);
+ }
+
+ @Test
+ public void testGetCropBoundsIfNeeded_appliesCrop() {
+ final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
+ InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
+
+ // Apply crop if taskbar is expanded
+ taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
+ assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+
+ mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
+ SCREEN_HEIGHT);
+
+ final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
+ assertNotEquals(null, crop);
+ assertEquals(0, crop.left);
+ assertEquals(0, crop.top);
+ assertEquals(mLetterboxedPortraitTaskBounds.width(), crop.right);
+ assertEquals(mLetterboxedPortraitTaskBounds.height() - TASKBAR_EXPANDED_HEIGHT,
+ crop.bottom);
+ }
+
+ @Test
+ public void testGetCropBoundsIfNeeded_appliesCropWithSizeCompatScaling() {
+ final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
+ InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
+ final float scaling = 2.0f;
+
+ // Apply crop if taskbar is expanded
+ taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
+ assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+ // With SizeCompat scaling
+ doReturn(true).when(mActivity).inSizeCompatMode();
+ mainWindow.mInvGlobalScale = scaling;
+
+ mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
+ SCREEN_HEIGHT);
+
+ final int appWidth = mLetterboxedPortraitTaskBounds.width();
+ final int appHeight = mLetterboxedPortraitTaskBounds.height();
+
+ final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
+ assertNotEquals(null, crop);
+ assertEquals(0, crop.left);
+ assertEquals(0, crop.top);
+ assertEquals((int) (appWidth * scaling), crop.right);
+ assertEquals((int) ((appHeight - TASKBAR_EXPANDED_HEIGHT) * scaling), crop.bottom);
+ }
+
+ @Test
+ public void testGetRoundedCornersRadius_withRoundedCornersFromInsets() {
+ final float invGlobalScale = 0.5f;
+ final int expectedRadius = 7;
+ final int configurationRadius = 15;
+
+ final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
+ mainWindow.mInvGlobalScale = invGlobalScale;
+ final InsetsState insets = mainWindow.getInsetsState();
+
+ RoundedCorners roundedCorners = new RoundedCorners(
+ /*topLeft=*/ null,
+ /*topRight=*/ null,
+ /*bottomRight=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT,
+ configurationRadius, /*centerX=*/ 1, /*centerY=*/ 1),
+ /*bottomLeft=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT,
+ configurationRadius * 2 /*2 is to test selection of the min radius*/,
+ /*centerX=*/ 1, /*centerY=*/ 1)
+ );
+ doReturn(roundedCorners).when(insets).getRoundedCorners();
+ mLetterboxConfiguration.setLetterboxActivityCornersRadius(-1);
+
+ assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+ }
+
+ @Test
+ public void testGetRoundedCornersRadius_withLetterboxActivityCornersRadius() {
+ final float invGlobalScale = 0.5f;
+ final int expectedRadius = 7;
+ final int configurationRadius = 15;
+
+ final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
+ mainWindow.mInvGlobalScale = invGlobalScale;
+ mLetterboxConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
+
+ assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+
+ }
+
+ @Test
+ public void testGetRoundedCornersRadius_noScalingApplied() {
+ final int configurationRadius = 15;
+
+ final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
+ mLetterboxConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
+
+ mainWindow.mInvGlobalScale = -1f;
+ assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+
+ mainWindow.mInvGlobalScale = 0f;
+ assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+
+ mainWindow.mInvGlobalScale = 1f;
+ assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+ }
+
+ private WindowState mockForGetCropBoundsAndRoundedCorners(@Nullable InsetsSource taskbar) {
+ final WindowState mainWindow = mock(WindowState.class);
+ final InsetsState insets = mock(InsetsState.class);
+ final Resources resources = mWm.mContext.getResources();
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+
+ mainWindow.mInvGlobalScale = 1f;
+ spyOn(resources);
+ spyOn(mActivity);
+
+ if (taskbar != null) {
+ taskbar.setVisible(true);
+ doReturn(taskbar).when(insets).peekSource(taskbar.getType());
+ }
+ doReturn(mLetterboxedPortraitTaskBounds).when(mActivity).getBounds();
+ doReturn(true).when(mActivity).isVisible();
+ doReturn(true).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
+ doReturn(insets).when(mainWindow).getInsetsState();
+ doReturn(attrs).when(mainWindow).getAttrs();
+ doReturn(true).when(mainWindow).isDrawn();
+ doReturn(false).when(mainWindow).isLetterboxedForDisplayCutout();
+ doReturn(true).when(mainWindow).areAppWindowBoundsLetterboxed();
+ doReturn(true).when(mLetterboxConfiguration).isLetterboxActivityCornersRounded();
+ doReturn(TASKBAR_EXPANDED_HEIGHT).when(resources).getDimensionPixelSize(
+ R.dimen.taskbar_frame_height);
+
+ // Need to reinitialise due to the change in resources getDimensionPixelSize output.
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ return mainWindow;
+ }
+
+ // overrideOrientationIfNeeded
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+ public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsPortrait()
+ throws Exception {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
+ public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsNosensor() {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_NOSENSOR);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
+ public void testOverrideOrientationIfNeeded_nosensorOverride_orientationFixed_returnsUnchanged() {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
+ public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationPortraitOrUndefined_returnsUnchanged() {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
+ public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationLandscape_returnsReverseLandscape() {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_LANDSCAPE),
+ SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+ public void testOverrideOrientationIfNeeded_portraitOverride_orientationFixed_returnsUnchanged() {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_NOSENSOR), SCREEN_ORIENTATION_NOSENSOR);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_portraitAndIgnoreFixedOverrides_returnsPortrait() {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_NOSENSOR), SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_noSensorAndIgnoreFixedOverrides_returnsNosensor() {
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_NOSENSOR);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+ public void testOverrideOrientationIfNeeded_propertyIsFalse_returnsUnchanged()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+
+ // shouldUseDisplayLandscapeNaturalOrientation
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+ public void testShouldUseDisplayLandscapeNaturalOrientation_override_returnsTrue() {
+ prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+ assertTrue(mController.shouldUseDisplayLandscapeNaturalOrientation());
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+ public void testShouldUseDisplayLandscapeNaturalOrientation_overrideAndFalseProperty_returnsFalse()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE, /* value */ false);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+ assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+ public void testShouldUseDisplayLandscapeNaturalOrientation_portraitNaturalOrientation_returnsFalse() {
+ prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+ doReturn(ORIENTATION_PORTRAIT).when(mDisplayContent).getNaturalOrientation();
+
+ assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+ public void testShouldUseDisplayLandscapeNaturalOrientation_disabledIgnoreOrientationRequest_returnsFalse() {
+ prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+ mDisplayContent.setIgnoreOrientationRequest(false);
+
+ assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+ public void testShouldUseDisplayLandscapeNaturalOrientation_inMultiWindowMode_returnsFalse() {
+ prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+
+ spyOn(mTask);
+ doReturn(true).when(mTask).inMultiWindowMode();
+
+ assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+ }
+
private void mockThatProperty(String propertyName, boolean value) throws Exception {
Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
/* className */ "");
@@ -316,6 +637,12 @@
doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
}
+ private void prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation() {
+ spyOn(mDisplayContent);
+ doReturn(ORIENTATION_LANDSCAPE).when(mDisplayContent).getNaturalOrientation();
+ mDisplayContent.setIgnoreOrientationRequest(true);
+ }
+
private void prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch() {
doReturn(true).when(mLetterboxConfiguration)
.isPolicyForIgnoringRequestedOrientationEnabled();
@@ -325,10 +652,10 @@
private ActivityRecord setUpActivityWithComponent() {
mDisplayContent = new TestDisplayContent
.Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
- Task task = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
+ mTask = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setOnTop(true)
- .setTask(task)
+ .setTask(mTask)
// Set the component to be that of the test class in order to enable compat changes
.setComponent(ComponentName.createRelative(mContext,
com.android.server.wm.LetterboxUiControllerTest.class.getName()))
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 08635ab..c131c84 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -468,7 +468,7 @@
mWm.setRecentsAnimationController(mController);
spyOn(mDisplayContent.mFixedRotationTransitionListener);
final ActivityRecord recents = mock(ActivityRecord.class);
- recents.mOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ recents.setOverrideOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
doReturn(ORIENTATION_PORTRAIT).when(recents)
.getRequestedConfigurationOrientation(anyBoolean());
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 3b7b5eb..91e875f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -102,7 +102,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.DeviceStateController.FoldState;
+import com.android.server.wm.DeviceStateController.DeviceState;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -2603,6 +2603,133 @@
}
@Test
+ public void testIsHorizontalReachabilityEnabled_splitScreen_false() {
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ setUpDisplaySizeWithApp(2800, 1000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+ // Unresizable portrait-only activity.
+ prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, 1400, 1000);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // Horizontal reachability is disabled because the app is in split screen.
+ assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+ }
+
+ @Test
+ public void testIsVerticalReachabilityEnabled_splitScreen_false() {
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ setUpDisplaySizeWithApp(1000, 2800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+ // Unresizable landscape-only activity.
+ prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_LANDSCAPE);
+
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, 1000, 1400);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // Vertical reachability is disabled because the app is in split screen.
+ assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+ }
+
+ @Test
+ public void testIsVerticalReachabilityEnabled_doesNotMatchParentWidth_false() {
+ setUpDisplaySizeWithApp(1000, 2800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+ // Unresizable landscape-only activity.
+ prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_LANDSCAPE);
+
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ // Activity now in size compat mode.
+ assertTrue(mActivity.inSizeCompatMode());
+
+ // Vertical reachability is disabled because the app does not match parent width
+ assertNotEquals(mActivity.getBounds().width(), mActivity.mDisplayContent.getBounds()
+ .width());
+ assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+ }
+
+ @Test
+ public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {
+ setUpDisplaySizeWithApp(2800, 1000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+
+ // Unresizable portrait-only activity.
+ prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ // Activity now in size compat mode.
+ assertTrue(mActivity.inSizeCompatMode());
+
+ // Horizontal reachability is disabled because the app does not match parent height
+ assertNotEquals(mActivity.getBounds().height(), mActivity.mDisplayContent.getBounds()
+ .height());
+ assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+ }
+
+ @Test
+ public void testIsHorizontalReachabilityEnabled_inSizeCompatMode_matchesParentHeight_true() {
+ setUpDisplaySizeWithApp(1800, 2200);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+
+ // Unresizable portrait-only activity.
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ // Activity now in size compat mode.
+ assertTrue(mActivity.inSizeCompatMode());
+
+ // Horizontal reachability is enabled because the app matches parent height
+ assertEquals(mActivity.getBounds().height(), mActivity.mDisplayContent.getBounds()
+ .height());
+ assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+ }
+
+ @Test
+ public void testIsVerticalReachabilityEnabled_inSizeCompatMode_matchesParentWidth_true() {
+ setUpDisplaySizeWithApp(2200, 1800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+ // Unresizable landscape-only activity.
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ // Activity now in size compat mode.
+ assertTrue(mActivity.inSizeCompatMode());
+
+ // Vertical reachability is enabled because the app matches parent width
+ assertEquals(mActivity.getBounds().width(), mActivity.mDisplayContent.getBounds().width());
+ assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+ }
+
+ @Test
public void testLetterboxDetailsForStatusBar_noLetterbox() {
setUpDisplaySizeWithApp(2800, 1000);
addStatusBar(mActivity.mDisplayContent);
@@ -2711,7 +2838,7 @@
mActivity.mRootWindowContainer.performSurfacePlacement();
final ArgumentCaptor<Rect> cropCapturer = ArgumentCaptor.forClass(Rect.class);
- verify(mTransaction, times(2)).setWindowCrop(
+ verify(mTransaction, times(2)).setCrop(
eq(w1.getSurfaceControl()),
cropCapturer.capture()
);
@@ -3075,9 +3202,9 @@
private void setFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
final DisplayRotation r = mActivity.mDisplayContent.getDisplayRotation();
doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
- doReturn(false).when(r).isDeviceInPosture(any(FoldState.class), anyBoolean());
+ doReturn(false).when(r).isDeviceInPosture(any(DeviceState.class), anyBoolean());
if (isHalfFolded) {
- doReturn(true).when(r).isDeviceInPosture(FoldState.HALF_FOLDED, isTabletop);
+ doReturn(true).when(r).isDeviceInPosture(DeviceState.HALF_FOLDED, isTabletop);
}
mActivity.recomputeConfiguration();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupContinuousTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupContinuousTest.java
index e6f47a1..eef7cc2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupContinuousTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupContinuousTest.java
@@ -22,6 +22,7 @@
import android.app.KeyguardManager;
import android.os.PowerManager;
+import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.cts.surfacevalidator.CapturedActivity;
import android.window.SurfaceSyncGroup;
@@ -68,11 +69,23 @@
@Test
public void testSurfaceControlViewHostIPCSync_Fast() throws Throwable {
- mCapturedActivity.verifyTest(new SyncValidatorSCVHTestCase(0 /* delayMs */), mName);
+ mCapturedActivity.verifyTest(
+ new SyncValidatorSCVHTestCase(0 /* delayMs */, false /* overrideDefaultDuration */),
+ mName);
}
@Test
public void testSurfaceControlViewHostIPCSync_Slow() throws Throwable {
- mCapturedActivity.verifyTest(new SyncValidatorSCVHTestCase(100 /* delayMs */), mName);
+ mCapturedActivity.verifyTest(new SyncValidatorSCVHTestCase(100 /* delayMs */,
+ false /* overrideDefaultDuration */), mName);
+ }
+
+ @Test
+ @Presubmit
+ public void testSurfaceControlViewHostIPCSync_Short() throws Throwable {
+ mCapturedActivity.setMinimumCaptureDurationMs(5000);
+ mCapturedActivity.verifyTest(
+ new SyncValidatorSCVHTestCase(0 /* delayMs */, true /* overrideDefaultDuration */),
+ mName);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index dbd7a4b..40e8273 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -513,7 +514,6 @@
assertApplyTransactionAllowed(mTransaction);
}
-
@Test
public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment() {
doReturn(true).when(mTaskFragment).isAttached();
@@ -836,7 +836,7 @@
final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
mOrganizerToken, fragmentToken1, activityAtBottom.token)
.setPairedActivityToken(activityAtBottom.token)
- .setInitialBounds(bounds)
+ .setInitialRelativeBounds(bounds)
.build();
mTransaction.setTaskFragmentOrganizer(mIOrganizer);
mTransaction.createTaskFragment(params);
@@ -1528,6 +1528,79 @@
assertEquals(TRANSIT_CHANGE, TASK_FRAGMENT_TRANSIT_CHANGE);
}
+ @Test
+ public void testApplyTransaction_setRelativeBounds() {
+ final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
+ ACTIVITY_TYPE_STANDARD);
+ mTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setFragmentToken(mFragmentToken)
+ .build();
+ final WindowContainerToken token = mTaskFragment.mRemoteToken.toWindowContainerToken();
+ final Rect relBounds = new Rect(0, 0, 100, 1000);
+ mTransaction.setRelativeBounds(token, relBounds);
+
+ // Not allowed because TaskFragment is not organized by the caller organizer.
+ assertApplyTransactionDisallowed(mTransaction);
+
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+
+ assertApplyTransactionAllowed(mTransaction);
+ assertEquals(relBounds, mTaskFragment.getRelativeEmbeddedBounds());
+ assertEquals(relBounds, mTaskFragment.getBounds());
+
+ // TaskFragment bounds should be updated to the same relative bounds.
+ final Rect taskBounds = new Rect(200, 200, 700, 1500);
+ task.setBoundsUnchecked(taskBounds);
+ assertEquals(taskBounds, task.getBounds());
+ assertEquals(relBounds, mTaskFragment.getRelativeEmbeddedBounds());
+ assertEquals(mTaskFragment.translateRelativeBoundsToAbsoluteBounds(relBounds, taskBounds),
+ mTaskFragment.getBounds());
+ assertEquals(new Rect(200, 200, 300, 1200), mTaskFragment.getBounds());
+
+ // Set TaskFragment to fill Task
+ mTransaction.setRelativeBounds(token, new Rect());
+
+ assertApplyTransactionAllowed(mTransaction);
+ assertEquals(new Rect(), mTaskFragment.getRelativeEmbeddedBounds());
+ assertEquals(taskBounds, mTaskFragment.getBounds());
+ }
+
+ @Test
+ public void testUntrustedEmbedding_setRelativeBounds_adjustToTaskBounds() {
+ final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
+ ACTIVITY_TYPE_STANDARD);
+ mTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setFragmentToken(mFragmentToken)
+ .build();
+ final WindowContainerToken token = mTaskFragment.mRemoteToken.toWindowContainerToken();
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+ // Untrusted embedded
+ doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode();
+
+ final Rect taskBounds = new Rect(0, 0, 500, 1000);
+ task.setBoundsUnchecked(taskBounds);
+
+ final Rect taskFragmentBounds = new Rect(250, 0, 750, 1000);
+ mTransaction.setRelativeBounds(token, taskFragmentBounds);
+
+ // Allow operation in case the Task is also resizing.
+ // Adjust the relBounds to the intersection.
+ assertApplyTransactionAllowed(mTransaction);
+ // Relative bounds is correctly set.
+ assertEquals(taskFragmentBounds, mTaskFragment.getRelativeEmbeddedBounds());
+ // The actual window bounds is adjusted to fit the Task bounds.
+ assertEquals(new Rect(250, 0, 500, 1000), mTaskFragment.getBounds());
+
+ // Adjust to the full requested bounds when the Task is resized.
+ taskBounds.set(0, 0, 750, 1000);
+ task.setBoundsUnchecked(taskBounds);
+ assertEquals(taskFragmentBounds, mTaskFragment.getBounds());
+ }
+
/**
* Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index c85671d..5099869 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -122,18 +122,22 @@
@Test
public void testShouldStartChangeTransition_relativePositionChange() {
+ final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
+ ACTIVITY_TYPE_STANDARD);
+ task.setBoundsUnchecked(new Rect(0, 0, 1000, 1000));
+ mTaskFragment = createTaskFragmentWithEmbeddedActivity(task, mOrganizer);
mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
final Rect startBounds = new Rect(0, 0, 500, 1000);
final Rect endBounds = new Rect(500, 0, 1000, 1000);
- mTaskFragment.setBounds(startBounds);
- mTaskFragment.updateRelativeEmbeddedBounds();
+ mTaskFragment.setRelativeEmbeddedBounds(startBounds);
+ mTaskFragment.recomputeConfiguration();
doReturn(true).when(mTaskFragment).isVisible();
doReturn(true).when(mTaskFragment).isVisibleRequested();
// Do not resize, just change the relative position.
final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds());
- mTaskFragment.setBounds(endBounds);
- mTaskFragment.updateRelativeEmbeddedBounds();
+ mTaskFragment.setRelativeEmbeddedBounds(endBounds);
+ mTaskFragment.recomputeConfiguration();
spyOn(mDisplayContent.mTransitionController);
// For Shell transition, we don't want to take snapshot when the bounds are not resized
@@ -150,19 +154,26 @@
@Test
public void testStartChangeTransition_resetSurface() {
+ final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
+ ACTIVITY_TYPE_STANDARD);
+ task.setBoundsUnchecked(new Rect(0, 0, 1000, 1000));
+ mTaskFragment = createTaskFragmentWithEmbeddedActivity(task, mOrganizer);
+ doReturn(mTransaction).when(mTaskFragment).getSyncTransaction();
+ doReturn(mTransaction).when(mTaskFragment).getPendingTransaction();
+ mLeash = mTaskFragment.getSurfaceControl();
mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
final Rect startBounds = new Rect(0, 0, 1000, 1000);
final Rect endBounds = new Rect(500, 500, 1000, 1000);
- mTaskFragment.setBounds(startBounds);
- mTaskFragment.updateRelativeEmbeddedBounds();
+ mTaskFragment.setRelativeEmbeddedBounds(startBounds);
+ mTaskFragment.recomputeConfiguration();
doReturn(true).when(mTaskFragment).isVisible();
doReturn(true).when(mTaskFragment).isVisibleRequested();
clearInvocations(mTransaction);
final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds());
mTaskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
- mTaskFragment.setBounds(endBounds);
- mTaskFragment.updateRelativeEmbeddedBounds();
+ mTaskFragment.setRelativeEmbeddedBounds(endBounds);
+ mTaskFragment.recomputeConfiguration();
assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds));
mTaskFragment.initializeChangeTransition(startBounds);
mTaskFragment.continueOrganizedTaskFragmentSurfaceUpdate();
@@ -201,8 +212,8 @@
mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
final Rect startBounds = new Rect(0, 0, 1000, 1000);
final Rect endBounds = new Rect(500, 500, 1000, 1000);
- mTaskFragment.setBounds(startBounds);
- mTaskFragment.updateRelativeEmbeddedBounds();
+ mTaskFragment.setRelativeEmbeddedBounds(startBounds);
+ mTaskFragment.recomputeConfiguration();
doReturn(true).when(mTaskFragment).isVisible();
doReturn(true).when(mTaskFragment).isVisibleRequested();
@@ -212,8 +223,8 @@
assertFalse(mTaskFragment.okToAnimate());
- mTaskFragment.setBounds(endBounds);
- mTaskFragment.updateRelativeEmbeddedBounds();
+ mTaskFragment.setRelativeEmbeddedBounds(endBounds);
+ mTaskFragment.recomputeConfiguration();
assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d31ae6a..83be4f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.Surface.ROTATION_0;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -78,6 +79,12 @@
final InputMonitor inputMonitor = getInputMonitor();
spyOn(inputMonitor);
doNothing().when(inputMonitor).resumeDispatchingLw(any());
+
+ // For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
+ // See DisplayRotation#readDefaultDisplayRotation for context.
+ // Without that, meaning of height and width in context of the tests can be swapped if
+ // the default rotation is 90 or 270.
+ displayRotation.setRotation(ROTATION_0);
}
public static class Builder {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 1a1ca54..5006bf7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -1665,7 +1665,7 @@
@Override
int getOrientation() {
- return getOrientation(super.mOrientation);
+ return getOrientation(super.getOverrideOrientation());
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/scvh/SyncValidatorSCVHTestCase.java b/services/tests/wmtests/src/com/android/server/wm/scvh/SyncValidatorSCVHTestCase.java
index af4c683c..07dac8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/scvh/SyncValidatorSCVHTestCase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/scvh/SyncValidatorSCVHTestCase.java
@@ -54,10 +54,12 @@
new Point(300, 800), new Point(200, 200)};
private int mLastSizeIndex = 1;
- private long mDelayMs;
+ private final long mDelayMs;
+ private final boolean mOverrideDefaultDuration;
- public SyncValidatorSCVHTestCase(long delayMs) {
+ public SyncValidatorSCVHTestCase(long delayMs, boolean overrideDefaultDuration) {
mDelayMs = delayMs;
+ mOverrideDefaultDuration = overrideDefaultDuration;
}
private final Runnable mRunnable = new Runnable() {
@@ -231,4 +233,9 @@
public void end() {
mHandler.removeCallbacks(mRunnable);
}
+
+ @Override
+ public boolean hasAnimation() {
+ return !mOverrideDefaultDuration;
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
index 3163820..20f03d8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.usage;
+import android.annotation.UserIdInt;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -35,65 +36,71 @@
public class UsageStatsIdleService extends JobService {
/**
- * Base job ID for the pruning job - must be unique within the system server uid.
+ * Namespace for prune job
*/
- private static final int PRUNE_JOB_ID = 546357475;
+ private static final String PRUNE_JOB_NS = "usagestats_prune";
+
/**
- * Job ID for the update mappings job - must be unique within the system server uid.
- * Incrementing PRUNE_JOB_ID by 21475 (MAX_USER_ID) to ensure there is no overlap in job ids.
+ * Namespace for update mappings job
*/
- private static final int UPDATE_MAPPINGS_JOB_ID = 546378950;
+ private static final String UPDATE_MAPPINGS_JOB_NS = "usagestats_mapping";
private static final String USER_ID_KEY = "user_id";
- static void scheduleJob(Context context, int userId) {
- final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
+ /** Schedule a prune job */
+ static void schedulePruneJob(Context context, @UserIdInt int userId) {
final ComponentName component = new ComponentName(context.getPackageName(),
UsageStatsIdleService.class.getName());
final PersistableBundle bundle = new PersistableBundle();
bundle.putInt(USER_ID_KEY, userId);
- final JobInfo pruneJob = new JobInfo.Builder(userJobId, component)
+ final JobInfo pruneJob = new JobInfo.Builder(userId, component)
.setRequiresDeviceIdle(true)
.setExtras(bundle)
.setPersisted(true)
.build();
- scheduleJobInternal(context, pruneJob, userJobId);
+ scheduleJobInternal(context, pruneJob, PRUNE_JOB_NS, userId);
}
- static void scheduleUpdateMappingsJob(Context context) {
+ static void scheduleUpdateMappingsJob(Context context, @UserIdInt int userId) {
final ComponentName component = new ComponentName(context.getPackageName(),
UsageStatsIdleService.class.getName());
- final JobInfo updateMappingsJob = new JobInfo.Builder(UPDATE_MAPPINGS_JOB_ID, component)
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(USER_ID_KEY, userId);
+ final JobInfo updateMappingsJob = new JobInfo.Builder(userId, component)
.setPersisted(true)
.setMinimumLatency(TimeUnit.DAYS.toMillis(1))
.setOverrideDeadline(TimeUnit.DAYS.toMillis(2))
+ .setExtras(bundle)
.build();
- scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_ID);
+ scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_NS, userId);
}
- private static void scheduleJobInternal(Context context, JobInfo pruneJob, int jobId) {
- final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
- final JobInfo pendingPruneJob = jobScheduler.getPendingJob(jobId);
- // only schedule a new prune job if one doesn't exist already for this user
- if (!pruneJob.equals(pendingPruneJob)) {
- jobScheduler.cancel(jobId); // cancel any previously scheduled prune job
- jobScheduler.schedule(pruneJob);
+ private static void scheduleJobInternal(Context context, JobInfo jobInfo,
+ String namespace, int jobId) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ jobScheduler = jobScheduler.forNamespace(namespace);
+ final JobInfo pendingJob = jobScheduler.getPendingJob(jobId);
+ // only schedule a new job if one doesn't exist already for this user
+ if (!jobInfo.equals(pendingJob)) {
+ jobScheduler.cancel(jobId); // cancel any previously scheduled job
+ jobScheduler.schedule(jobInfo);
}
}
- static void cancelJob(Context context, int userId) {
- cancelJobInternal(context, PRUNE_JOB_ID + userId);
+ static void cancelPruneJob(Context context, @UserIdInt int userId) {
+ cancelJobInternal(context, PRUNE_JOB_NS, userId);
}
- static void cancelUpdateMappingsJob(Context context) {
- cancelJobInternal(context, UPDATE_MAPPINGS_JOB_ID);
+ static void cancelUpdateMappingsJob(Context context, @UserIdInt int userId) {
+ cancelJobInternal(context, UPDATE_MAPPINGS_JOB_NS, userId);
}
- private static void cancelJobInternal(Context context, int jobId) {
- final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ private static void cancelJobInternal(Context context, String namespace, int jobId) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
if (jobScheduler != null) {
+ jobScheduler = jobScheduler.forNamespace(namespace);
jobScheduler.cancel(jobId);
}
}
@@ -102,15 +109,19 @@
public boolean onStartJob(JobParameters params) {
final PersistableBundle bundle = params.getExtras();
final int userId = bundle.getInt(USER_ID_KEY, -1);
- if (userId == -1 && params.getJobId() != UPDATE_MAPPINGS_JOB_ID) {
+
+ if (userId == -1) { // legacy job
return false;
}
+ // Do async
AsyncTask.execute(() -> {
final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
UsageStatsManagerInternal.class);
- if (params.getJobId() == UPDATE_MAPPINGS_JOB_ID) {
- final boolean jobFinished = usageStatsManagerInternal.updatePackageMappingsData();
+ final String jobNs = params.getJobNamespace();
+ if (UPDATE_MAPPINGS_JOB_NS.equals(jobNs)) {
+ final boolean jobFinished =
+ usageStatsManagerInternal.updatePackageMappingsData(userId);
jobFinished(params, !jobFinished); // reschedule if data was not updated
} else {
final boolean jobFinished =
@@ -118,6 +129,8 @@
jobFinished(params, !jobFinished); // reschedule if data was not pruned
}
});
+
+ // Job is running asynchronously
return true;
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b3a1f2b..7ff5b4a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -433,11 +433,9 @@
private void onUserUnlocked(int userId) {
// fetch the installed packages outside the lock so it doesn't block package manager.
final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
- // delay updating of package mappings for user 0 since their data is not likely to be stale.
- // this also makes it less likely for restored data to be erased on unexpected reboots.
- if (userId == UserHandle.USER_SYSTEM) {
- UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
- }
+
+ UsageStatsIdleService.scheduleUpdateMappingsJob(getContext(), userId);
+
final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId));
synchronized (mLock) {
// This should be safe to add this early. Other than reportEventOrAddToQueue and
@@ -1261,8 +1259,8 @@
}
mAppStandby.onUserRemoved(userId);
// Cancel any scheduled jobs for this user since the user is being removed.
- UsageStatsIdleService.cancelJob(getContext(), userId);
- UsageStatsIdleService.cancelUpdateMappingsJob(getContext());
+ UsageStatsIdleService.cancelPruneJob(getContext(), userId);
+ UsageStatsIdleService.cancelUpdateMappingsJob(getContext(), userId);
}
/**
@@ -1300,7 +1298,7 @@
// Schedule a job to prune any data related to this package.
if (tokenRemoved != PackagesTokenData.UNASSIGNED_TOKEN) {
- UsageStatsIdleService.scheduleJob(getContext(), userId);
+ UsageStatsIdleService.schedulePruneJob(getContext(), userId);
}
}
@@ -1325,19 +1323,19 @@
/**
* Called by the Binder stub.
*/
- private boolean updatePackageMappingsData() {
+ private boolean updatePackageMappingsData(@UserIdInt int userId) {
// don't update the mappings if a profile user is defined
- if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) {
+ if (!shouldDeleteObsoleteData(UserHandle.of(userId))) {
return true; // return true so job scheduler doesn't reschedule the job
}
// fetch the installed packages outside the lock so it doesn't block package manager.
- final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
+ final HashMap<String, Long> installedPkgs = getInstalledPackages(userId);
synchronized (mLock) {
- if (!mUserUnlockedStates.contains(UserHandle.USER_SYSTEM)) {
+ if (!mUserUnlockedStates.contains(userId)) {
return false; // user is no longer unlocked
}
- final UserUsageStatsService userService = mUserState.get(UserHandle.USER_SYSTEM);
+ final UserUsageStatsService userService = mUserState.get(userId);
if (userService == null) {
return false; // user was stopped or removed
}
@@ -3055,44 +3053,35 @@
}
@Override
- public byte[] getBackupPayload(int user, String key) {
- if (!mUserUnlockedStates.contains(user)) {
- Slog.w(TAG, "Failed to get backup payload for locked user " + user);
+ public byte[] getBackupPayload(@UserIdInt int userId, String key) {
+ if (!mUserUnlockedStates.contains(userId)) {
+ Slog.w(TAG, "Failed to get backup payload for locked user " + userId);
return null;
}
synchronized (mLock) {
- // Check to ensure that only user 0's data is b/r for now
- // Note: if backup and restore is enabled for users other than the system user, the
- // #onUserUnlocked logic, specifically when the update mappings job is scheduled via
- // UsageStatsIdleService.scheduleUpdateMappingsJob, will have to be updated.
- if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
- if (userStats == null) {
- return null; // user was stopped or removed
- }
- return userStats.getBackupPayload(key);
- } else {
- return null;
+ final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId);
+ if (userStats == null) {
+ return null; // user was stopped or removed
}
+ Slog.i(TAG, "Returning backup payload for u=" + userId);
+ return userStats.getBackupPayload(key);
}
}
@Override
- public void applyRestoredPayload(int user, String key, byte[] payload) {
+ public void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload) {
synchronized (mLock) {
- if (!mUserUnlockedStates.contains(user)) {
- Slog.w(TAG, "Failed to apply restored payload for locked user " + user);
+ if (!mUserUnlockedStates.contains(userId)) {
+ Slog.w(TAG, "Failed to apply restored payload for locked user " + userId);
return;
}
- if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
- if (userStats == null) {
- return; // user was stopped or removed
- }
- final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
- mAppStandby.restoreAppsToRare(restoredApps, user);
+ final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId);
+ if (userStats == null) {
+ return; // user was stopped or removed
}
+ final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
+ mAppStandby.restoreAppsToRare(restoredApps, userId);
}
}
@@ -3165,8 +3154,8 @@
}
@Override
- public boolean updatePackageMappingsData() {
- return UsageStatsService.this.updatePackageMappingsData();
+ public boolean updatePackageMappingsData(@UserIdInt int userId) {
+ return UsageStatsService.this.updatePackageMappingsData(userId);
}
/**
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 28d726e..1a1af3b 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -866,7 +866,8 @@
public void simulateDisplayPortAltModeInfo(String portId, int partnerSinkStatus,
- int cableStatus, int numLanes, IndentingPrintWriter pw) {
+ int cableStatus, int numLanes, boolean hpd, int linkTrainingStatus,
+ IndentingPrintWriter pw) {
synchronized (mLock) {
final RawPortInfo portInfo = mSimulatedPorts.get(portId);
if (portInfo == null) {
@@ -875,7 +876,8 @@
}
DisplayPortAltModeInfo displayPortAltModeInfo =
- new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes);
+ new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes, hpd,
+ linkTrainingStatus);
portInfo.displayPortAltModeInfo = displayPortAltModeInfo;
pw.println("Simulating DisplayPort Info: " + displayPortAltModeInfo);
updatePortsLocked(pw, null);
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 7d84222..cad1f6f 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
+import static android.hardware.usb.DisplayPortAltModeInfo.LINK_TRAINING_STATUS_UNKNOWN;
import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
@@ -1169,14 +1171,17 @@
mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
"", 0);
}
- } else if ("set-displayport-status".equals(args[0]) && args.length == 5) {
+ } else if ("set-displayport-status".equals(args[0]) && args.length == 7) {
final String portId = args[1];
final int partnerSinkStatus = Integer.parseInt(args[2]);
final int cableStatus = Integer.parseInt(args[3]);
final int displayPortNumLanes = Integer.parseInt(args[4]);
+ final boolean hpd = Boolean.parseBoolean(args[5]);
+ final int linkTrainingStatus = Integer.parseInt(args[6]);
if (mPortManager != null) {
mPortManager.simulateDisplayPortAltModeInfo(portId,
- partnerSinkStatus, cableStatus, displayPortNumLanes, pw);
+ partnerSinkStatus, cableStatus, displayPortNumLanes,
+ hpd, linkTrainingStatus, pw);
pw.println();
mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
"", 0);
@@ -1185,9 +1190,12 @@
final String portId = args[1];
if (mPortManager != null) {
mPortManager.simulateDisplayPortAltModeInfo(portId,
- DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
- DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
- 0, pw);
+ DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
+ DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
+ 0,
+ false,
+ LINK_TRAINING_STATUS_UNKNOWN,
+ pw);
pw.println();
mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
"", 0);
@@ -1259,7 +1267,13 @@
pw.println("Example simulate DisplayPort Alt Mode Changes:");
pw.println(" dumpsys usb add-port \"matrix\" dual --displayport");
pw.println(" dumpsys usb set-displayport-status \"matrix\" <partner-sink>"
- + " <cable> <num-lanes>");
+ + " <cable> <num-lanes> <hpd> <link-training-status>");
+ pw.println("The required fields are as followed:");
+ pw.println(" <partner-sink>: type DisplayPortAltModeStatus");
+ pw.println(" <cable>: type DisplayPortAltModeStatus");
+ pw.println(" <num-lanes>: type int, expected 0, 2, or 4");
+ pw.println(" <hpd>: type boolean, expected true or false");
+ pw.println(" <link-training-status>: type LinkTrainingStatus");
pw.println(" dumpsys usb reset-displayport-status \"matrix\"");
pw.println("reset-displayport-status can also be used in order to set");
pw.println("the DisplayPortInfo to default values.");
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
index 49b9d7b..d367f99 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
@@ -62,6 +62,9 @@
+ " Length: " + getLength());
canvas.openList();
canvas.writeListItem("" + getNumJacks() + " Jacks.");
+ for (int i = 0; i < getNumJacks(); i++) {
+ canvas.writeListItem("Jack " + i + ": " + mJackIds[i]);
+ }
canvas.closeList();
}
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
index 1024a5b..16248b8 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
@@ -62,6 +62,9 @@
+ " Length: " + getLength());
canvas.openList();
canvas.writeListItem("" + getNumGroupTerminals() + " Group Terminals.");
+ for (int i = 0; i < getNumGroupTerminals(); i++) {
+ canvas.writeListItem("Group Terminal " + i + ": " + mBlockIds[i]);
+ }
canvas.closeList();
}
}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
index b9ccace..c7a7a9b 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -639,7 +639,9 @@
altModeData.getDisplayPortAltModeData();
return new DisplayPortAltModeInfo(displayPortData.partnerSinkStatus,
displayPortData.cableStatus,
- toDisplayPortAltModeNumLanesInt(displayPortData.pinAssignment));
+ toDisplayPortAltModeNumLanesInt(displayPortData.pinAssignment),
+ displayPortData.hpd,
+ displayPortData.linkTrainingStatus);
}
}
return null;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
index e5d0370..01b2cb9 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
elaurent@google.com
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AidlUtil.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AidlUtil.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/AudioSessionProviderImpl.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AudioSessionProviderImpl.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/AudioSessionProviderImpl.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AudioSessionProviderImpl.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/Dumpable.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/Dumpable.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalException.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/HalException.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/HalException.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/HalException.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/HalFactory.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/HalFactory.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/OWNERS b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/OWNERS
new file mode 100644
index 0000000..01b2cb9
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/OWNERS
@@ -0,0 +1,2 @@
+atneya@google.com
+elaurent@google.com
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/README.md b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/README.md
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/README.md
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/README.md
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/RecoverableException.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/RecoverableException.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/RecoverableException.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/RecoverableException.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/TEST_MAPPING b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/UuidUtil.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/UuidUtil.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/UuidUtil.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/UuidUtil.java
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
similarity index 100%
rename from services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
rename to services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index b8536f9..afee940 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -17,6 +17,8 @@
package com.android.server.voiceinteraction;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.LOG_COMPAT_CHANGE;
+import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
@@ -43,11 +45,14 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_SECURITY_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK;
+import static com.android.server.voiceinteraction.HotwordDetectionConnection.ENFORCE_HOTWORD_PHRASE_ID;
import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.AppOpsManager;
+import android.app.compat.CompatChanges;
import android.attention.AttentionManagerInternal;
import android.content.Context;
import android.content.PermissionChecker;
@@ -692,8 +697,13 @@
}
}
- static void enforceExtraKeyphraseIdNotLeaked(HotwordDetectedResult result,
+ @RequiresPermission(allOf = {READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE})
+ void enforceExtraKeyphraseIdNotLeaked(HotwordDetectedResult result,
SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) {
+ if (!CompatChanges.isChangeEnabled(ENFORCE_HOTWORD_PHRASE_ID,
+ mVoiceInteractionServiceUid)) {
+ return;
+ }
// verify the phrase ID in HotwordDetectedResult is not exposing extra phrases
// the DSP did not detect
for (SoundTrigger.KeyphraseRecognitionExtra keyphrase : recognitionEvent.keyphraseExtras) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 665d5e7..445446e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -29,6 +29,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -39,6 +41,7 @@
import android.media.AudioManagerInternal;
import android.media.permission.Identity;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -48,6 +51,7 @@
import android.os.ServiceManager;
import android.os.SharedMemory;
import android.provider.DeviceConfig;
+import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
@@ -83,6 +87,26 @@
private static final String TAG = "HotwordDetectionConnection";
static final boolean DEBUG = false;
+ /**
+ * For apps targeting Android API {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above,
+ * implementors of the {@link HotwordDetectionService} must not augment the phrase IDs which are
+ * supplied via {@link HotwordDetectionService
+ * #onDetect(AlwaysOnHotwordDetector.EventPayload, long, HotwordDetectionService.Callback)}.
+ *
+ * <p>The {@link HotwordDetectedResult#getHotwordPhraseId()} must match one of the phrase IDs
+ * from the {@link android.service.voice.AlwaysOnHotwordDetector
+ * .EventPayload#getKeyphraseRecognitionExtras()} list.
+ * </p>
+ *
+ * <p>This behavior change is made to ensure the {@link HotwordDetectionService} honors what
+ * it receives from the {@link android.hardware.soundtrigger.SoundTriggerModule}, and it
+ * cannot signal to the client application a phrase which was not origially detected.
+ * </p>
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final long ENFORCE_HOTWORD_PHRASE_ID = 215066299L;
+
private static final String KEY_RESTART_PERIOD_IN_SECONDS = "restart_period_in_seconds";
private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
private static final int MAX_ISOLATED_PROCESS_NUMBER = 10;
@@ -108,7 +132,7 @@
final int mUser;
final Context mContext;
volatile HotwordDetectionServiceIdentity mIdentity;
- //TODO: add similar identity for visual query service for the use of content capturing
+ //TODO: Consider rename this to SandboxedDetectionIdentity
private Instant mLastRestartInstant;
private ScheduledFuture<?> mDebugHotwordLoggingTimeoutFuture = null;
@@ -117,6 +141,7 @@
@GuardedBy("mLock")
@Nullable
private final Identity mVoiceInteractorIdentity;
+ private int mRestartCount = 0;
@NonNull private ServiceConnection mRemoteHotwordDetectionService;
@NonNull private ServiceConnection mRemoteVisualQueryDetectionService;
private IBinder mAudioFlinger;
@@ -234,10 +259,6 @@
mDebugHotwordLogging = false;
unbindVisualQueryDetectionService();
unbindHotwordDetectionService();
- if (mIdentity != null) {
- removeServiceUidForAudioPolicy(mIdentity.getIsolatedUid());
- }
- mIdentity = null;
if (mCancellationTaskFuture != null) {
mCancellationTaskFuture.cancel(/* mayInterruptIfRunning= */ true);
}
@@ -246,18 +267,34 @@
}
}
+ @SuppressWarnings("GuardedBy")
private void unbindVisualQueryDetectionService() {
if (mRemoteVisualQueryDetectionService != null) {
mRemoteVisualQueryDetectionService.unbind();
- //TODO: Set visual query detection service provider to null
+ mRemoteVisualQueryDetectionService = null;
}
+ resetDetectionProcessIdentityIfEmptyLocked();
}
+ @SuppressWarnings("GuardedBy")
private void unbindHotwordDetectionService() {
if (mRemoteHotwordDetectionService != null) {
mRemoteHotwordDetectionService.unbind();
+ mRemoteHotwordDetectionService = null;
+ }
+ resetDetectionProcessIdentityIfEmptyLocked();
+ }
+
+ // TODO(b/266669849): Clean up SuppressWarnings for calling methods.
+ @GuardedBy("mLock")
+ private void resetDetectionProcessIdentityIfEmptyLocked() {
+ if (mRemoteHotwordDetectionService == null && mRemoteVisualQueryDetectionService == null) {
LocalServices.getService(PermissionManagerServiceInternal.class)
.setHotwordDetectionServiceProvider(null);
+ if (mIdentity != null) {
+ removeServiceUidForAudioPolicy(mIdentity.getIsolatedUid());
+ }
+ mIdentity = null;
}
}
@@ -427,14 +464,20 @@
ServiceConnection oldHotwordConnection = mRemoteHotwordDetectionService;
ServiceConnection oldVisualQueryDetectionConnection = mRemoteVisualQueryDetectionService;
HotwordDetectionServiceIdentity previousIdentity = mIdentity;
- //TODO: Add previousIdentity for visual query detection service
mLastRestartInstant = Instant.now();
// Recreate connection to reset the cache.
+ mRestartCount++;
- mRemoteHotwordDetectionService = mHotwordDetectionServiceConnectionFactory.createLocked();
- mRemoteVisualQueryDetectionService =
- mVisualQueryDetectionServiceConnectionFactory.createLocked();
+ if (oldHotwordConnection != null) {
+ mRemoteHotwordDetectionService =
+ mHotwordDetectionServiceConnectionFactory.createLocked();
+ }
+
+ if (oldVisualQueryDetectionConnection != null) {
+ mRemoteVisualQueryDetectionService =
+ mVisualQueryDetectionServiceConnectionFactory.createLocked();
+ }
Slog.v(TAG, "Started the new process, dispatching processRestarted to detector");
runForEachDetectorSessionLocked((session) -> {
@@ -458,6 +501,9 @@
oldVisualQueryDetectionConnection.unbind();
}
+ // TODO(b/266670431): Handles identity resetting for the new process to make sure the
+ // correct identity is provided.
+
if (previousIdentity != null) {
removeServiceUidForAudioPolicy(previousIdentity.getIsolatedUid());
}
@@ -531,8 +577,7 @@
&& mRemoteHotwordDetectionService != null
&& mRemoteHotwordDetectionService.isBound());
pw.print(prefix); pw.print("mRestartCount=");
- pw.println(mHotwordDetectionServiceConnectionFactory.mRestartCount);
- pw.println(mVisualQueryDetectionServiceConnectionFactory.mRestartCount);
+ pw.println(mRestartCount);
pw.print(prefix); pw.print("mLastRestartInstant="); pw.println(mLastRestartInstant);
pw.print(prefix); pw.print("mDetectorType=");
pw.println(HotwordDetector.detectorTypeToString(mDetectorType));
@@ -547,8 +592,6 @@
private final Intent mIntent;
private final int mBindingFlags;
- private int mRestartCount = 0;
-
ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed) {
mIntent = intent;
mBindingFlags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0;
@@ -558,7 +601,7 @@
ServiceConnection connection =
new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
ISandboxedDetectionService.Stub::asInterface,
- mRestartCount++ % MAX_ISOLATED_PROCESS_NUMBER);
+ mRestartCount % MAX_ISOLATED_PROCESS_NUMBER);
connection.connect();
updateAudioFlinger(connection, mAudioFlinger);
@@ -660,13 +703,10 @@
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE,
mVoiceInteractionServiceUid);
}
- String instancePrefix =
- mIntent.getAction().equals(HotwordDetectionService.SERVICE_INTERFACE)
- ? "hotword_detector_" : "visual_query_detector_";
boolean bindResult = mContext.bindIsolatedService(
mIntent,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | mBindingFlags,
- instancePrefix + mInstanceNumber,
+ "hotword_detector_" + mInstanceNumber,
mExecutor,
serviceConnection);
if (!bindResult) {
diff --git a/telecomm/java/android/telecom/CallEndpointException.java b/telecomm/java/android/telecom/CallEndpointException.java
index e2238928..994e1c9 100644
--- a/telecomm/java/android/telecom/CallEndpointException.java
+++ b/telecomm/java/android/telecom/CallEndpointException.java
@@ -92,25 +92,12 @@
public @interface CallEndpointErrorCode {
}
- public CallEndpointException(@Nullable String message) {
- super(getMessage(message, ERROR_UNSPECIFIED));
- mMessage = message;
- }
-
public CallEndpointException(@Nullable String message, @CallEndpointErrorCode int code) {
super(getMessage(message, code));
mCode = code;
mMessage = message;
}
- public CallEndpointException(@Nullable String message, @CallEndpointErrorCode int code,
- @Nullable Throwable cause) {
- super(getMessage(message, code), cause);
- mCode = code;
- mMessage = message;
- }
-
-
public @CallEndpointErrorCode int getCode() {
return mCode;
}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index fd2907c..95a8e16 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -32,10 +32,8 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
/**
* A unified virtual device providing a means of voice (and other) communication on a device.
@@ -150,14 +148,6 @@
private final Object mLock = new Object();
- // Future used to delay terminating the InCallService before the call disconnect tone
- // finishes playing.
- private static Map<String, CompletableFuture<Void>> sDisconnectedToneFutures = new ArrayMap<>();
-
- // Timeout value to be used to ensure future completion for sDisconnectedToneFutures. This is
- // set to 4 seconds to account for the exceptional case (TONE_CONGESTION).
- private static final int DISCONNECTED_TONE_TIMEOUT = 4000;
-
Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
mInCallAdapter = adapter;
mCallingPackage = callingPackage;
@@ -466,45 +456,9 @@
}
private void fireCallRemoved(Call call) {
- String callId = call.internalGetCallId();
- CompletableFuture<Void> disconnectedToneFuture = initializeDisconnectedToneFuture(callId);
- // delay the InCallService termination until after the disconnect tone finishes playing
- disconnectedToneFuture.thenRunAsync(() -> {
- for (Listener listener : mListeners) {
- listener.onCallRemoved(this, call);
- }
- // clean up the future after
- sDisconnectedToneFutures.remove(callId);
- });
- }
-
- /**
- * Initialize disconnect tone future to be used in delaying ICS termination.
- *
- * @return CompletableFuture to delay InCallService termination until after the disconnect tone
- * finishes playing. A timeout of 4s is used to handle the use case when we play
- * TONE_CONGESTION and to ensure completion so that we don't block the removal of the service.
- */
- private CompletableFuture<Void> initializeDisconnectedToneFuture(String callId) {
- // create the future and map (sDisconnectedToneFutures) it to the corresponding call id
- CompletableFuture<Void> disconnectedToneFuture = new CompletableFuture<Void>()
- .completeOnTimeout(null, DISCONNECTED_TONE_TIMEOUT, TimeUnit.MILLISECONDS);
- // we should not encounter duplicate insertions since call ids are unique
- sDisconnectedToneFutures.put(callId, disconnectedToneFuture);
- return disconnectedToneFuture;
- }
-
- /**
- * Completes disconnected tone future with passed in result.
- * @hide
- * @return true if future was completed, false otherwise
- */
- public static boolean completeDisconnectedToneFuture(String callId) {
- if (sDisconnectedToneFutures.containsKey(callId)) {
- sDisconnectedToneFutures.get(callId).complete(null);
- return true;
+ for (Listener listener : mListeners) {
+ listener.onCallRemoved(this, call);
}
- return false;
}
private void fireCallAudioStateChanged(CallAudioState audioState) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index ca15422..b6def1a 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -592,6 +592,11 @@
/**
* Sets the address. See {@link PhoneAccount#getAddress}.
+ * <p>
+ * Note: The entire URI value is limited to 256 characters. This check is
+ * enforced when registering the PhoneAccount via
+ * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an
+ * {@link IllegalArgumentException} to be thrown if URI is over 256.
*
* @param value The address of the phone account.
* @return The builder.
@@ -625,6 +630,10 @@
/**
* Sets the icon. See {@link PhoneAccount#getIcon}.
+ * <p>
+ * Note: An {@link IllegalArgumentException} if the Icon cannot be written to memory.
+ * This check is enforced when registering the PhoneAccount via
+ * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}
*
* @param icon The icon to set.
*/
@@ -663,6 +672,10 @@
/**
* Specifies an additional URI scheme supported by the {@link PhoneAccount}.
*
+ * <p>
+ * Each URI scheme is limited to 256 characters. Adding a scheme over 256 characters will
+ * cause an {@link IllegalArgumentException} to be thrown when the account is registered.
+ *
* @param uriScheme The URI scheme.
* @return The builder.
*/
@@ -676,6 +689,12 @@
/**
* Specifies the URI schemes supported by the {@link PhoneAccount}.
*
+ * <p>
+ * A max of 10 URI schemes can be added per account. Additionally, each URI scheme is
+ * limited to 256 characters. Adding more than 10 URI schemes or 256 characters on any
+ * scheme will cause an {@link IllegalArgumentException} to be thrown when the account
+ * is registered.
+ *
* @param uriSchemes The URI schemes.
* @return The builder.
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7950351..f0f230f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -34,6 +34,7 @@
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.SaProposal;
import android.os.Build;
+import android.os.Handler;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.service.carrier.CarrierService;
@@ -49,6 +50,7 @@
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
@@ -65,7 +67,7 @@
@SystemService(Context.CARRIER_CONFIG_SERVICE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public class CarrierConfigManager {
- private final static String TAG = "CarrierConfigManager";
+ private static final String TAG = "CarrierConfigManager";
/**
* Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the slot index that the
@@ -108,25 +110,25 @@
* Only send USSD over IMS while CS is out of service, otherwise send USSD over CS.
* {@link #KEY_CARRIER_USSD_METHOD_INT}
*/
- public static final int USSD_OVER_CS_PREFERRED = 0;
+ public static final int USSD_OVER_CS_PREFERRED = 0;
/**
* Send USSD over IMS or CS while IMS is out of service or silent redial over CS if needed.
* {@link #KEY_CARRIER_USSD_METHOD_INT}
*/
- public static final int USSD_OVER_IMS_PREFERRED = 1;
+ public static final int USSD_OVER_IMS_PREFERRED = 1;
/**
* Only send USSD over CS.
* {@link #KEY_CARRIER_USSD_METHOD_INT}
*/
- public static final int USSD_OVER_CS_ONLY = 2;
+ public static final int USSD_OVER_CS_ONLY = 2;
/**
* Only send USSD over IMS and disallow silent redial over CS.
* {@link #KEY_CARRIER_USSD_METHOD_INT}
*/
- public static final int USSD_OVER_IMS_ONLY = 3;
+ public static final int USSD_OVER_IMS_ONLY = 3;
/**
* Indicates CARRIER_NR_AVAILABILITY_NSA determine that the carrier enable the non-standalone
@@ -161,8 +163,8 @@
* @see TelephonyManager#getSimCarrierId()
* @see TelephonyManager#getSimSpecificCarrierId()
*/
- public static final String
- ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ public static final String ACTION_CARRIER_CONFIG_CHANGED =
+ "android.telephony.action.CARRIER_CONFIG_CHANGED";
// Below are the keys used in carrier config bundles. To add a new variable, define the key and
// give it a default value in sDefaults. If you need to ship a per-network override in the
@@ -183,8 +185,8 @@
* @deprecated Use {@link Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL} instead.
*/
@Deprecated
- public static final String
- KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL =
+ "carrier_volte_provisioned_bool";
/**
* Boolean indicating the Supplementary Services(SS) is disable when airplane mode on in the
@@ -217,29 +219,29 @@
public static final String KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL =
"call_forwarding_when_unreachable_supported_bool";
- /**
- * Boolean indicating if carrier supports call forwarding option "When unanswered".
- *
- * {@code true}: Call forwarding option "When unanswered" is supported.
- * {@code false}: Call forwarding option "When unanswered" is not supported. Option will be
- * removed in the UI.
- *
- * By default this value is true.
- * @hide
- */
+ /**
+ * Boolean indicating if carrier supports call forwarding option "When unanswered".
+ *
+ * {@code true}: Call forwarding option "When unanswered" is supported.
+ * {@code false}: Call forwarding option "When unanswered" is not supported. Option will be
+ * removed in the UI.
+ *
+ * By default this value is true.
+ * @hide
+ */
public static final String KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL =
"call_forwarding_when_unanswered_supported_bool";
- /**
- * Boolean indicating if carrier supports call forwarding option "When busy".
- *
- * {@code true}: Call forwarding option "When busy" is supported.
- * {@code false}: Call forwarding option "When busy" is not supported. Option will be
- * removed in the UI.
- *
- * By default this value is true.
- * @hide
- */
+ /**
+ * Boolean indicating if carrier supports call forwarding option "When busy".
+ *
+ * {@code true}: Call forwarding option "When busy" is supported.
+ * {@code false}: Call forwarding option "When busy" is not supported. Option will be
+ * removed in the UI.
+ *
+ * By default this value is true.
+ * @hide
+ */
public static final String KEY_CALL_FORWARDING_WHEN_BUSY_SUPPORTED_BOOL =
"call_forwarding_when_busy_supported_bool";
@@ -259,12 +261,12 @@
public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL =
"additional_settings_call_waiting_visibility_bool";
- /**
- * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
- * If true, the "Call Barring" menu will be visible. If false, the menu will be gone.
- *
- * Disabled by default.
- */
+ /**
+ * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+ * 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";
@@ -321,8 +323,8 @@
* If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and
* effectively disable the "Sim network lock" feature.
*/
- public static final String
- KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
+ public static final String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL =
+ "ignore_sim_network_locked_events_bool";
/**
* When checking if a given number is the voicemail number, if this flag is true
@@ -340,16 +342,15 @@
* consequence: there will be no way to make an Emergency Call if your SIM is network-locked and
* you don't know the PIN.)
*/
- public static final String
- KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL =
+ "sim_network_unlock_allow_dismiss_bool";
/**
* Flag indicating whether or not sending emergency SMS messages over IMS
* is supported when in LTE/limited LTE (Emergency only) service mode..
- *
*/
- public static final String
- KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
+ public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL =
+ "support_emergency_sms_over_ims_bool";
/** Flag indicating if the phone is a world phone */
public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
@@ -359,8 +360,8 @@
* If true, entitlement checks will be executed if device has been configured for it,
* If false, entitlement checks will be skipped.
*/
- public static final String
- KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
+ public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL =
+ "require_entitlement_checks_bool";
/**
* Flag indicating if the carrier supports tethering of mobile data.
@@ -392,8 +393,8 @@
* consistent with the regular Dialer, this value should agree with the corresponding values
* from config.xml under apps/Contacts.
*/
- public static final String
- KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+ public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL =
+ "enable_dialer_key_vibration_bool";
/** Flag indicating if dtmf tone type is enabled */
public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -416,11 +417,12 @@
* @hide
*/
public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
+
/**
* Determines if the carrier requires converting the destination number before sending out an
* SMS. Certain networks and numbering plans require different formats.
*/
- public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL=
+ public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL =
"sms_requires_destination_number_conversion_bool";
/**
@@ -428,7 +430,8 @@
* platforms, even the ones with hard SEND/END keys, but for maximum flexibility it's controlled
* by a flag here (which can be overridden on a per-product basis.)
*/
- public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
+ public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL =
+ "show_onscreen_dial_button_bool";
/** Determines if device implements a noise suppression device for in call audio. */
public static final String
@@ -440,8 +443,8 @@
* accidental redialing from the call log UI. This is a good idea, so the default here is
* false.)
*/
- public static final String
- KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
+ public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL =
+ "allow_emergency_numbers_in_call_log_bool";
/**
* A string array containing numbers that shouldn't be included in the call log.
@@ -467,10 +470,9 @@
* Flag indicating whether to show single operator row in the choose network setting.
*
* The device configuration value {@code config_enableNewAutoSelectNetworkUI} ultimately
- * controls whether this carrier configuration option is used. Where
- * {@code config_enableNewAutoSelectNetworkUI} is false, the value of the
- * {@link #KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL} carrier configuration
- * option is ignored.
+ * controls whether this carrier configuration option is used.
+ * Where {@code config_enableNewAutoSelectNetworkUI} is false, the value of this
+ * carrier configuration is ignored.
*
* If {@code true}, default value, merge the duplicate networks which with the same plmn, keep
* the one that with the higher signal strength level.
@@ -498,18 +500,18 @@
* @deprecated Never implemented. Has no behavior impact when override. DO NOT USE.
*/
@Deprecated
- public static final String
- KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
+ public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL =
+ "simplified_network_settings_bool";
/** Control whether users can reach the SIM lock settings. */
- public static final String
- KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
+ public static final String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
/** Control whether users can edit APNs in Settings. */
public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
/** Control whether users can choose a network operator. */
- public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
+ public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL =
+ "operator_selection_expand_bool";
/**
* Used in the Preferred Network Types menu to determine if the 2G option is displayed.
@@ -537,7 +539,8 @@
* TODO: This should be combined with config_use_hfa_for_provisioning and implemented as an enum
* (NONE, HFA, OTASP).
*/
- public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+ public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL =
+ "use_otasp_for_provisioning_bool";
/** Display carrier settings menu if true */
public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
@@ -562,25 +565,26 @@
* available the user cannot use voicemail. This flag allows the user to edit the voicemail
* number in such cases, and is false by default.
*/
- public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL= "editable_voicemail_number_bool";
+ public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL =
+ "editable_voicemail_number_bool";
/**
* Determine whether the voicemail notification is persistent in the notification bar. If true,
* the voicemail notifications cannot be dismissed from the notification bar.
*/
- public static final String
- KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
+ public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL =
+ "voicemail_notification_persistent_bool";
/** For IMS video over LTE calls, determines whether video pause signalling is supported. */
- public static final String
- KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
+ public static final String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL =
+ "support_pause_ims_video_calls_bool";
/**
* Disables dialing "*228" (OTASP provisioning) on CDMA carriers where it is not supported or is
* potentially harmful by locking the SIM to 3G.
*/
- public static final String
- KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL =
+ "disable_cdma_activation_code_bool";
/**
* List of network type constants which support only a single data connection at a time.
@@ -588,8 +592,8 @@
* @see TelephonyManager NETWORK_TYPE_*
* @see #KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY
*/
- public static final String
- KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
+ public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY =
+ "only_single_dc_allowed_int_array";
/**
* Only apply if {@link #KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY} specifies the network types that
@@ -604,15 +608,15 @@
* Override the platform's notion of a network operator being considered roaming.
* Value is string array of MCCMNCs to be considered roaming for 3GPP RATs.
*/
- public static final String
- KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
+ public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY =
+ "gsm_roaming_networks_string_array";
/**
* Override the platform's notion of a network operator being considered not roaming.
* Value is string array of MCCMNCs to be considered not roaming for 3GPP RATs.
*/
- public static final String
- KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
+ public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY =
+ "gsm_nonroaming_networks_string_array";
/**
* The package name containing the ImsService that will be bound to the telephony framework to
@@ -622,6 +626,7 @@
* {@link #KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING} instead to configure these values
* separately. If any of those values are not empty, they will override this value.
*/
+ @Deprecated
public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING =
"config_ims_package_override_string";
@@ -668,7 +673,7 @@
/**
* Override the platform's notion of a network operator being considered non roaming.
- * If true all networks are considered as home network a.k.a non-roaming. When false,
+ * If true all networks are considered as home network a.k.a. non-roaming. When false,
* the 2 pairs of CMDA and GSM roaming/non-roaming arrays are consulted.
*
* @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
@@ -703,8 +708,7 @@
* <li>3: {@link #USSD_OVER_IMS_ONLY} </li>
* </ul>
*/
- public static final String KEY_CARRIER_USSD_METHOD_INT =
- "carrier_ussd_method_int";
+ public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
/**
* Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE.
@@ -735,8 +739,7 @@
* downgrading the call in the process.
* @hide
*/
- public static final String KEY_ALLOW_MERGING_RTT_CALLS_BOOL =
- "allow_merging_rtt_calls_bool";
+ public static final String KEY_ALLOW_MERGING_RTT_CALLS_BOOL = "allow_merging_rtt_calls_bool";
/**
* Flag specifying whether the carrier wants to notify the user when a VT call has been handed
@@ -797,7 +800,7 @@
/**
* When {@code true}, changes to the mobile data enabled switch will not cause the VT
- * registration state to change. That is, turning on or off mobile data will not cause VT to be
+ * registration state to change. That is, turning on or off mobile data will not cause VT to be
* enabled or disabled.
* When {@code false}, disabling mobile data will cause VT to be de-registered.
*/
@@ -820,7 +823,8 @@
* carrier provisioning. If false: hard disabled. If true: then depends on carrier
* provisioning, availability etc.
*/
- public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+ public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL =
+ "carrier_wfc_ims_available_bool";
/**
* Flag specifying whether Cross SIM over IMS should be available for carrier.
@@ -860,8 +864,8 @@
"international_roaming_dial_string_replace_string_array";
/**
- * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi
- * calling settings will not include an option for "wifi only". If true, the wifi calling
+ * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi
+ * calling settings will not include an option for "wifi only". If true, the wifi calling
* settings will include an option for "wifi only"
* <p>
* By default, it is assumed that WFC supports "wifi only".
@@ -904,7 +908,7 @@
/**
* Flag indicating whether failed calls due to no service should prompt the user to enable
- * WIFI calling. When {@code true}, if the user attempts to establish a call when there is no
+ * WIFI calling. When {@code true}, if the user attempts to establish a call when there is no
* service available, they are connected to WIFI, and WIFI calling is disabled, a different
* call failure message will be used to encourage the user to enable WIFI calling.
* @hide
@@ -930,8 +934,8 @@
* {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE}
*/
@Deprecated
- public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
- = "carrier_volte_provisioning_required_bool";
+ public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL =
+ "carrier_volte_provisioning_required_bool";
/**
* Flag indicating whether or not the IMS MmTel UT capability requires carrier provisioning
@@ -974,22 +978,22 @@
* As of now, Verizon is the only carrier enforcing this dependency in their
* WFC awareness and activation requirements.
*/
- public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL
- = "carrier_volte_override_wfc_provisioning_bool";
+ public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL =
+ "carrier_volte_override_wfc_provisioning_bool";
/**
* Override the device's configuration for the cellular data service to use for this SIM card.
* @hide
*/
- public static final String KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
- = "carrier_data_service_wwan_package_override_string";
+ public static final String KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING =
+ "carrier_data_service_wwan_package_override_string";
/**
* Override the device's configuration for the IWLAN data service to use for this SIM card.
* @hide
*/
- public static final String KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
- = "carrier_data_service_wlan_package_override_string";
+ public static final String KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING =
+ "carrier_data_service_wlan_package_override_string";
/**
* Override the device's configuration for the cellular data service class to use
@@ -1008,8 +1012,8 @@
"carrier_data_service_wlan_class_override_string";
/** Flag specifying whether VoLTE TTY is supported. */
- public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
- = "carrier_volte_tty_supported_bool";
+ public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL =
+ "carrier_volte_tty_supported_bool";
/** Flag specifying whether VoWIFI TTY is supported.
* @hide
@@ -1021,37 +1025,37 @@
* Flag specifying whether IMS service can be turned off. If false then the service will not be
* turned-off completely, but individual features can be disabled.
*/
- public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL
- = "carrier_allow_turnoff_ims_bool";
+ public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL =
+ "carrier_allow_turnoff_ims_bool";
/**
* Flag specifying whether Generic Bootstrapping Architecture capable SIM is required for IMS.
*/
- public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
- = "carrier_ims_gba_required_bool";
+ public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL =
+ "carrier_ims_gba_required_bool";
/**
- * Flag specifying whether IMS instant lettering is available for the carrier. {@code True} if
+ * Flag specifying whether IMS instant lettering is available for the carrier. {@code True} if
* instant lettering is available for the carrier, {@code false} otherwise.
*/
public static final String KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL =
"carrier_instant_lettering_available_bool";
- /*
+ /**
* Flag specifying whether IMS should be the first phone attempted for E911 even if the
* phone is not in service.
*/
- public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL
- = "carrier_use_ims_first_for_emergency_bool";
+ public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL =
+ "carrier_use_ims_first_for_emergency_bool";
/**
* When {@code true}, the determination of whether to place a call as an emergency call will be
* based on the known {@link android.telephony.emergency.EmergencyNumber}s for the SIM on which
- * the call is being placed. In a dual SIM scenario, if Sim A has the emergency numbers
+ * the call is being placed. In a dual SIM scenario, if Sim A has the emergency numbers
* 123, 456 and Sim B has the emergency numbers 789, and the user places a call on SIM A to 789,
* it will not be treated as an emergency call in this case.
* When {@code false}, the determination is based on the emergency numbers from all device SIMs,
- * regardless of which SIM the call is being placed on. If Sim A has the emergency numbers
+ * regardless of which SIM the call is being placed on. If Sim A has the emergency numbers
* 123, 456 and Sim B has the emergency numbers 789, and the user places a call on SIM A to 789,
* the call will be dialed as an emergency number, but with an unspecified routing.
* @hide
@@ -1062,7 +1066,7 @@
/**
* When IMS instant lettering is available for a carrier (see
* {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the list of characters
- * which may not be contained in messages. Should be specified as a regular expression suitable
+ * which may not be contained in messages. Should be specified as a regular expression suitable
* for use with {@link String#matches(String)}.
*/
public static final String KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING =
@@ -1071,8 +1075,8 @@
/**
* When IMS instant lettering is available for a carrier (see
* {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines a list of characters which
- * must be escaped with a backslash '\' character. Should be specified as a string containing
- * the characters to be escaped. For example to escape quote and backslash the string would be
+ * must be escaped with a backslash '\' character. Should be specified as a string containing
+ * the characters to be escaped. For example to escape quote and backslash the string would be
* a quote and a backslash.
*/
public static final String KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING =
@@ -1081,10 +1085,10 @@
/**
* When IMS instant lettering is available for a carrier (see
* {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the character encoding
- * which will be used when determining the length of messages. Used in the InCall UI to limit
- * the number of characters the user may type. If empty-string, the instant lettering
- * message size limit will be enforced on a 1:1 basis. That is, each character will count
- * towards the messages size limit as a single bye. If a character encoding is specified, the
+ * which will be used when determining the length of messages. Used in the InCall UI to limit
+ * the number of characters the user may type. If empty-string, the instant lettering
+ * message size limit will be enforced on a 1:1 basis. That is, each character will count
+ * towards the messages size limit as a single byte. If a character encoding is specified, the
* message size limit will be based on the number of bytes in the message per the specified
* encoding.
*/
@@ -1093,7 +1097,7 @@
/**
* When IMS instant lettering is available for a carrier (see
- * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), the length limit for messages. Used
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), the length limit for messages. Used
* in the InCall UI to ensure the user cannot enter more characters than allowed by the carrier.
* See also {@link #KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING} for more information on how
* the length of the message is calculated.
@@ -1114,7 +1118,8 @@
* manager can control and route outgoing and incoming phone calls, even if they're placed
* using another connection service (PSTN, for example).
*/
- public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
+ public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING =
+ "default_sim_call_manager_string";
/**
* The default flag specifying whether ETWS/CMAS test setting is forcibly disabled in
@@ -1217,8 +1222,7 @@
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
*/
- public static final String KEY_CARRIER_ERI_FILE_NAME_STRING =
- "carrier_eri_file_name_string";
+ public static final String KEY_CARRIER_ERI_FILE_NAME_STRING = "carrier_eri_file_name_string";
/* The following 3 fields are related to carrier visual voicemail. */
@@ -1242,13 +1246,12 @@
* Whether cellular data is required to access visual voicemail.
*/
public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL =
- "vvm_cellular_data_required_bool";
+ "vvm_cellular_data_required_bool";
/**
* The default OMTP visual voicemail client prefix to use. Defaulted to "//VVM"
*/
- public static final String KEY_VVM_CLIENT_PREFIX_STRING =
- "vvm_client_prefix_string";
+ public static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
/**
* Whether to use SSL to connect to the visual voicemail IMAP server. Defaulted to false.
@@ -1273,8 +1276,7 @@
* <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
* function without the data cost.
*/
- public static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL =
- "vvm_legacy_mode_enabled_bool";
+ public static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL = "vvm_legacy_mode_enabled_bool";
/**
* Whether to prefetch audio data on new voicemail arrival, defaulted to true.
@@ -1288,7 +1290,8 @@
* @deprecated use {@link #KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY}.
*/
@Deprecated
- public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+ public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING =
+ "carrier_vvm_package_name_string";
/**
* A list of the carrier's visual voicemail app package names to ensure that dialer visual
@@ -1307,7 +1310,7 @@
* Status screen. The default value is true.
*/
public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL =
- "show_signal_strength_in_sim_status_bool";
+ "show_signal_strength_in_sim_status_bool";
/**
* Flag specifying if we should interpret all signal strength as one bar higher
@@ -1386,9 +1389,8 @@
public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
"ignore_rtt_mode_setting_bool";
-
/**
- * Determines whether adhoc conference calls are supported by a carrier. When {@code true},
+ * Determines whether adhoc conference calls are supported by a carrier. When {@code true},
* adhoc conference calling is supported, {@code false otherwise}.
*/
public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
@@ -1396,14 +1398,14 @@
/**
* Determines whether conference participants can be added to existing call to form an adhoc
- * conference call (in contrast to merging calls to form a conference). When {@code true},
+ * conference call (in contrast to merging calls to form a conference). When {@code true},
* adding conference participants to existing call is supported, {@code false otherwise}.
*/
public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL =
"support_add_conference_participants_bool";
/**
- * Determines whether conference calls are supported by a carrier. When {@code true},
+ * Determines whether conference calls are supported by a carrier. When {@code true},
* conference calling is supported, {@code false otherwise}.
*/
public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
@@ -1411,7 +1413,7 @@
/**
* Determines whether a maximum size limit for IMS conference calls is enforced on the device.
* When {@code true}, IMS conference calls will be limited to at most
- * {@link #KEY_IMS_CONFERENCE_SIZE_LIMIT_INT} participants. When {@code false}, no attempt is
+ * {@link #KEY_IMS_CONFERENCE_SIZE_LIMIT_INT} participants. When {@code false}, no attempt is
* made to limit the number of participants in a conference (the carrier will raise an error
* when an attempt is made to merge too many participants into a conference).
* <p>
@@ -1425,14 +1427,14 @@
/**
* Determines the maximum number of participants the carrier supports for a conference call.
- * This number is exclusive of the current device. A conference between 3 devices, for example,
+ * This number is exclusive of the current device. A conference between 3 devices, for example,
* would have a size limit of 2 participants.
* Enforced when {@link #KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL} is {@code true}.
*/
public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
/**
- * Determines whether manage IMS conference calls is supported by a carrier. When {@code true},
+ * Determines whether manage IMS conference calls is supported by a carrier. When {@code true},
* manage IMS conference call is supported, {@code false otherwise}.
* @hide
*/
@@ -1455,7 +1457,7 @@
* and B and C are considered the conference peers.
* <p>
* When {@code true}, the conference peer will display the conference state if it receives
- * conference event package data from the network. When {@code false}, the conference peer will
+ * conference event package data from the network. When {@code false}, the conference peer will
* ignore conference event package data received from the network.
* @hide
*/
@@ -1473,7 +1475,7 @@
/**
* Indicates whether the carrier supports the negotiations of RFC8285 compliant RTP header
- * extensions supported on a call during the Session Description Protocol (SDP). This option
+ * extensions supported on a call during the Session Description Protocol (SDP). This option
* is only used when {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is
* {@code true}.
* <p>
@@ -1503,7 +1505,7 @@
"display_hd_audio_property_bool";
/**
- * Determines whether IMS conference calls are supported by a carrier. When {@code true},
+ * Determines whether IMS conference calls are supported by a carrier. When {@code true},
* IMS conference calling is supported, {@code false} otherwise.
* @hide
*/
@@ -1512,9 +1514,9 @@
/**
* Determines whether the device will locally disconnect an IMS conference when the participant
- * count drops to zero. When {@code true}, it is assumed the carrier does NOT disconnect a
+ * count drops to zero. When {@code true}, it is assumed the carrier does NOT disconnect a
* conference when the participant count drops to zero and that the device must do this by
- * disconnecting the conference locally. When {@code false}, it is assumed that the carrier
+ * disconnecting the conference locally. When {@code false}, it is assumed that the carrier
* is responsible for disconnecting the conference when there are no longer any participants
* present.
* <p>
@@ -1530,8 +1532,8 @@
"local_disconnect_empty_ims_conference_bool";
/**
- * Determines whether video conference calls are supported by a carrier. When {@code true},
- * video calls can be merged into conference calls, {@code false} otherwiwse.
+ * Determines whether video conference calls are supported by a carrier. When {@code true},
+ * video calls can be merged into conference calls, {@code false} otherwise.
* <p>
* Note: even if video conference calls are not supported, audio calls may be merged into a
* conference if {@link #KEY_SUPPORT_CONFERENCE_CALL_BOOL} is {@code true}.
@@ -1568,7 +1570,8 @@
/**
* Determine whether preferred network type can be shown.
*/
- public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+ public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL =
+ "hide_preferred_network_type_bool";
/**
* String array for package names that need to be enabled for this carrier.
@@ -1800,20 +1803,20 @@
public static final String KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING =
"sim_country_iso_override_string";
- /**
- * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
- * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
- * the carrier
- * CallScreeningService with the opportunity to allow or block calls.
- * <p>
- * The String includes the package name/the class name.
- * Example:
- * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
- * <p>
- * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
- * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
- * ComponentName.
- */
+ /**
+ * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
+ * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
+ * the carrier
+ * CallScreeningService with the opportunity to allow or block calls.
+ * <p>
+ * The String includes the package name/the class name.
+ * Example:
+ * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
+ * <p>
+ * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
+ * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
+ * ComponentName.
+ */
public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
/**
@@ -1925,20 +1928,20 @@
"broadcast_emergency_call_state_changes_bool";
/**
- * Indicates whether STK LAUNCH_BROWSER command is disabled.
- * If {@code true}, then the browser will not be launched
- * on UI for the LAUNCH_BROWSER STK command.
- * @hide
- */
+ * Indicates whether STK LAUNCH_BROWSER command is disabled.
+ * If {@code true}, then the browser will not be launched
+ * on UI for the LAUNCH_BROWSER STK command.
+ * @hide
+ */
public static final String KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL =
"stk_disable_launch_browser_bool";
/**
- * Boolean indicating if the helper text for STK GET INKEY/INPUT commands with the digit only
- * mode is displayed on the input screen.
- * The helper text is dispayed regardless of the input mode, if {@code false}.
- * @hide
- */
+ * Boolean indicating if the helper text for STK GET INKEY/INPUT commands with the digit only
+ * mode is displayed on the input screen.
+ * The helper text is displayed regardless of the input mode, if {@code false}.
+ * @hide
+ */
public static final String KEY_HIDE_DIGITS_HELPER_TEXT_ON_STK_INPUT_SCREEN_BOOL =
"hide_digits_helper_text_on_stk_input_screen_bool";
@@ -2006,6 +2009,19 @@
"include_lte_for_nr_advanced_threshold_bandwidth_bool";
/**
+ * Indicating whether to ratchet the aggregated cell bandwidths on receiving new values when
+ * the device is in RRC IDLE mode.
+ * The aggregated cell bandwidths are used for determining NR advanced state.
+ *
+ * If this is {@code true}, we will only update the aggregate cell bandwidths if the new
+ * aggregate is higher than the current aggregate and the anchor NR cell is the same.
+ * If this is {@code false}, we will always update the aggregate cell bandwidths when receiving
+ * new values.
+ */
+ public static final String KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL =
+ "ratchet_nr_advanced_bandwidth_if_rrc_idle_bool";
+
+ /**
* Boolean indicating if operator name should be shown in the status bar
* @hide
*/
@@ -2085,16 +2101,22 @@
public static final String KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL = "allowAttachAudio";
public static final String KEY_MMS_APPEND_TRANSACTION_ID_BOOL = "enabledTransID";
public static final String KEY_MMS_GROUP_MMS_ENABLED_BOOL = "enableGroupMms";
- public static final String KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL = "enableMMSDeliveryReports";
+ public static final String KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL =
+ "enableMMSDeliveryReports";
public static final String KEY_MMS_MMS_ENABLED_BOOL = "enabledMMS";
public static final String KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL = "enableMMSReadReports";
public static final String KEY_MMS_MULTIPART_SMS_ENABLED_BOOL = "enableMultipartSMS";
public static final String KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL = "enabledNotifyWapMMSC";
- public static final String KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL = "sendMultipartSmsAsSeparateMessages";
- public static final String KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL = "config_cellBroadcastAppLinks";
- public static final String KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL = "enableSMSDeliveryReports";
- public static final String KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL = "supportHttpCharsetHeader";
- public static final String KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL = "supportMmsContentDisposition";
+ public static final String KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL =
+ "sendMultipartSmsAsSeparateMessages";
+ public static final String KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL =
+ "config_cellBroadcastAppLinks";
+ public static final String KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL =
+ "enableSMSDeliveryReports";
+ public static final String KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL =
+ "supportHttpCharsetHeader";
+ public static final String KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL =
+ "supportMmsContentDisposition";
public static final String KEY_MMS_ALIAS_MAX_CHARS_INT = "aliasMaxChars";
public static final String KEY_MMS_ALIAS_MIN_CHARS_INT = "aliasMinChars";
public static final String KEY_MMS_HTTP_SOCKET_TIMEOUT_INT = "httpSocketTimeout";
@@ -2103,7 +2125,8 @@
public static final String KEY_MMS_MAX_MESSAGE_SIZE_INT = "maxMessageSize";
public static final String KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT = "maxMessageTextSize";
public static final String KEY_MMS_RECIPIENT_LIMIT_INT = "recipientLimit";
- public static final String KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT = "smsToMmsTextLengthThreshold";
+ public static final String KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT =
+ "smsToMmsTextLengthThreshold";
public static final String KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT = "smsToMmsTextThreshold";
public static final String KEY_MMS_SUBJECT_MAX_LENGTH_INT = "maxSubjectLength";
public static final String KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING = "emailGatewayNumber";
@@ -2132,7 +2155,7 @@
* The flatten {@link android.content.ComponentName componentName} of the activity that can
* setup the device and activate with the network per carrier requirements.
*
- * e.g, com.google.android.carrierPackageName/.CarrierActivityName
+ * e.g., com.google.android.carrierPackageName/.CarrierActivityName
* @hide
*/
@SystemApi
@@ -2273,7 +2296,7 @@
/**
* Determines whether the carrier supports making non-emergency phone calls while the phone is
- * in emergency callback mode. Default value is {@code true}, meaning that non-emergency calls
+ * in emergency callback mode. Default value is {@code true}, meaning that non-emergency calls
* are allowed in emergency callback mode.
*/
public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL =
@@ -2296,7 +2319,7 @@
/**
* Flag indicating whether to allow carrier video calls to emergency numbers.
- * When {@code true}, video calls to emergency numbers will be allowed. When {@code false},
+ * When {@code true}, video calls to emergency numbers will be allowed. When {@code false},
* video calls to emergency numbers will be initiated as audio-only calls instead.
*/
public static final String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL =
@@ -2324,7 +2347,7 @@
* When presence is supported, the device should use the
* {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
* {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
- * whether each contact supports video calling. The UI is made aware that presence is enabled
+ * whether each contact supports video calling. The UI is made aware that presence is enabled
* via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
* and can choose to hide or show the video calling icon based on whether a contact supports
* video.
@@ -2348,7 +2371,7 @@
* contacts emergency services. Platform considers values for below cases:
* 1) 0 <= VALUE <= 604800(one week): the value will be used as the duration directly.
* 2) VALUE > 604800(one week): will use the default value as duration instead.
- * 3) VALUE < 0: block will be disabled forever until user re-eanble block manually,
+ * 3) VALUE < 0: block will be disabled forever until user re-enable block manually,
* the suggested value to disable forever is -1.
* See {@code android.provider.BlockedNumberContract#notifyEmergencyContact(Context)}
* See {@code android.provider.BlockedNumberContract#isBlocked(Context, String)}.
@@ -2378,7 +2401,7 @@
/**
* For carriers which require an empty flash to be sent before sending the normal 3-way calling
- * flash, the duration in milliseconds of the empty flash to send. When {@code 0}, no empty
+ * flash, the duration in milliseconds of the empty flash to send. When {@code 0}, no empty
* flash is sent.
*/
public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
@@ -2418,8 +2441,7 @@
* Int indicating the max number length for FDN
* @hide
*/
- public static final String KEY_FDN_NUMBER_LENGTH_LIMIT_INT =
- "fdn_number_length_limit_int";
+ public static final String KEY_FDN_NUMBER_LENGTH_LIMIT_INT = "fdn_number_length_limit_int";
/**
* Report IMEI as device id even if it's a CDMA/LTE phone.
@@ -2431,16 +2453,15 @@
/**
* The families of Radio Access Technologies that will get clustered and ratcheted,
* ie, we will report transitions up within the family, but not down until we change
- * cells. This prevents flapping between base technologies and higher techs that are
+ * cells. This prevents flapping between base technologies and higher techs that are
* granted on demand within the cell.
* @hide
*/
- public static final String KEY_RATCHET_RAT_FAMILIES =
- "ratchet_rat_families";
+ public static final String KEY_RATCHET_RAT_FAMILIES = "ratchet_rat_families";
/**
* Flag indicating whether some telephony logic will treat a call which was formerly a video
- * call as if it is still a video call. When {@code true}:
+ * call as if it is still a video call. When {@code true}:
* <p>
* Logic which will automatically drop a video call which takes place over WIFI when a
* voice call is answered (see {@link #KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL}.
@@ -2452,7 +2473,7 @@
/**
* When {@code true}, if the user is in an ongoing video call over WIFI and answers an incoming
- * audio call, the video call will be disconnected before the audio call is answered. This is
+ * audio call, the video call will be disconnected before the audio call is answered. This is
* in contrast to the usual expected behavior where a foreground video call would be put into
* the background and held when an incoming audio call is answered.
*/
@@ -2462,8 +2483,8 @@
/**
* Flag indicating whether the carrier supports merging wifi calls when VoWIFI is disabled.
* This can happen in the case of a carrier which allows offloading video calls to WIFI
- * separately of whether voice over wifi is enabled. In such a scenario when two video calls
- * are downgraded to voice, they remain over wifi. However, if VoWIFI is disabled, these calls
+ * separately of whether voice over wifi is enabled. In such a scenario when two video calls
+ * are downgraded to voice, they remain over wifi. However, if VoWIFI is disabled, these calls
* cannot be merged.
*/
public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL =
@@ -2473,9 +2494,9 @@
* Flag indicating whether the carrier supports the Hold command while in an IMS call.
* <p>
* The device configuration value {@code config_device_respects_hold_carrier_config} ultimately
- * controls whether this carrier configuration option is used. Where
- * {@code config_device_respects_hold_carrier_config} is false, the value of the
- * {@link #KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} carrier configuration option is ignored.
+ * controls whether this carrier configuration option is used.
+ * Where {@code config_device_respects_hold_carrier_config} is false, the value of
+ * this carrier configuration is ignored.
* @hide
*/
public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
@@ -2530,8 +2551,7 @@
* <p>
* This is {@code true} by default.
*/
- public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL =
- "allow_hold_video_call_bool";
+ public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL = "allow_hold_video_call_bool";
/**
* When true, indicates that the HD audio icon in the in-call screen should not be shown for
@@ -2625,7 +2645,8 @@
* is returned.
* @hide
*/
- public static final String KEY_FILTERED_CNAP_NAMES_STRING_ARRAY = "filtered_cnap_names_string_array";
+ public static final String KEY_FILTERED_CNAP_NAMES_STRING_ARRAY =
+ "filtered_cnap_names_string_array";
/**
* The RCS configuration server URL. This URL is used to initiate RCS provisioning.
@@ -2693,9 +2714,9 @@
"emergency_notification_delay_int";
/**
- * When {@code true}, the carrier allows the user of the
- * {@link TelephonyManager#sendUssdRequest(String, TelephonyManager.UssdResponseCallback,
- * Handler)} API to perform USSD requests. {@code True} by default.
+ * When {@code true}, the carrier allows the user of the {@link
+ * TelephonyManager#sendUssdRequest(String, TelephonyManager.UssdResponseCallback, Handler)}
+ * API to perform USSD requests. {@code True} by default.
* @hide
*/
public static final String KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL =
@@ -2707,7 +2728,7 @@
* fails.
*/
public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL =
- "support_3gpp_call_forwarding_while_roaming_bool";
+ "support_3gpp_call_forwarding_while_roaming_bool";
/**
* Boolean indicating whether to display voicemail number as default call forwarding number in
@@ -2773,8 +2794,7 @@
* This setting may be still overridden by explicit user choice. By default,
* {@link #DATA_CYCLE_USE_PLATFORM_DEFAULT} will be used.
*/
- public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT =
- "monthly_data_cycle_day_int";
+ public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
/**
* When {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG},
@@ -2820,7 +2840,7 @@
/**
* Controls if the device should automatically warn the user that sim voice & data function
- * might be limited due to dual sim scenario. When set to {@true} display the notification,
+ * might be limited due to dual sim scenario. When set to {@code true} display the notification,
* {@code false} otherwise.
* @hide
*/
@@ -2846,16 +2866,14 @@
* their cellular data limit. When set to {@code false} the carrier is
* expected to have implemented their own notification mechanism. {@code true} by default.
*/
- public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL =
- "data_limit_notification_bool";
+ public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
/**
* Controls if the device should automatically notify the user when rapid
* cellular data usage is observed. When set to {@code false} the carrier is
- * expected to have implemented their own notification mechanism. {@code true} by default.
+ * expected to have implemented their own notification mechanism. {@code true} by default.
*/
- public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL =
- "data_rapid_notification_bool";
+ public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool";
/**
* Offset to be reduced from rsrp threshold while calculating signal strength level.
@@ -2888,8 +2906,7 @@
* "nrarfcn2_start-nrarfcn2_end" ... }
* @hide
*/
- public static final String KEY_BOOSTED_NRARFCNS_STRING_ARRAY =
- "boosted_nrarfcns_string_array";
+ public static final String KEY_BOOSTED_NRARFCNS_STRING_ARRAY = "boosted_nrarfcns_string_array";
/**
* Determine whether to use only RSRP for the number of LTE signal bars.
@@ -2996,8 +3013,8 @@
* is set, the default value 2 is used.
* @hide
*/
- public static final String
- KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT = "ngran_ssrsrp_hysteresis_db_int";
+ public static final String KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT =
+ "ngran_ssrsrp_hysteresis_db_int";
/**
* An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_SSRSRQ} measurement
@@ -3005,10 +3022,10 @@
*
* <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
* is set, the default value 2 is used.
- *@hide
+ * @hide
*/
- public static final String
- KEY_NGRAN_SSRSRQ_HYSTERESIS_DB_INT = "ngran_ssrsrq_hysteresis_db_int";
+ public static final String KEY_NGRAN_SSRSRQ_HYSTERESIS_DB_INT =
+ "ngran_ssrsrq_hysteresis_db_int";
/**
* An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_SSSINR} measurement
@@ -3018,8 +3035,8 @@
* is set, the default value 2 is used.
* @hide
*/
- public static final String
- KEY_NGRAN_SSSINR_HYSTERESIS_DB_INT = "ngran_sssinr_hysteresis_db_int";
+ public static final String KEY_NGRAN_SSSINR_HYSTERESIS_DB_INT =
+ "ngran_sssinr_hysteresis_db_int";
/**
* Bit-field integer to determine whether to use SS reference signal received power (SSRSRP),
@@ -3107,8 +3124,7 @@
* A match on this supersedes a match on {@link #KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
* @hide
*/
- public static final String KEY_ROAMING_OPERATOR_STRING_ARRAY =
- "roaming_operator_string_array";
+ public static final String KEY_ROAMING_OPERATOR_STRING_ARRAY = "roaming_operator_string_array";
/**
* URL from which the proto containing the public key of the Carrier used for
@@ -3177,7 +3193,7 @@
* Boolean flag indicating whether the carrier supports TTY.
* <p>
* Note that {@link #KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL} controls availability of TTY over
- * VoLTE; if {@link #KEY_TTY_SUPPORTED_BOOL} is disabled, then
+ * VoLTE; if this carrier configuration is disabled, then
* {@link #KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL} is also implicitly disabled.
* <p>
* {@link TelecomManager#isTtySupported()} should be used to determine if a device supports TTY,
@@ -3301,11 +3317,11 @@
public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL =
"check_pricing_with_carrier_data_roaming_bool";
- /**
- * Determines whether we should show a notification when the phone established a data
- * connection in roaming network, to warn users about possible roaming charges.
- * @hide
- */
+ /**
+ * Determines whether we should show a notification when the phone established a data
+ * connection in roaming network, to warn users about possible roaming charges.
+ * @hide
+ */
public static final String KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL =
"show_data_connected_roaming_notification";
@@ -3320,8 +3336,7 @@
* these boundaries is considered invalid.
* @hide
*/
- public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
- "lte_rsrp_thresholds_int_array";
+ public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY = "lte_rsrp_thresholds_int_array";
/**
* A list of 4 customized LTE Reference Signal Received Quality (RSRQ) thresholds.
@@ -3338,8 +3353,7 @@
* This key is considered invalid if the format is violated. If the key is invalid or
* not configured, a default value set will apply.
*/
- public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY =
- "lte_rsrq_thresholds_int_array";
+ public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
/**
* A list of 4 customized LTE Reference Signal Signal to Noise Ratio (RSSNR) thresholds.
@@ -3365,8 +3379,7 @@
* is set, the default value 2 is used.
* @hide
*/
- public static final String
- KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT = "eutran_rsrp_hysteresis_db_int";
+ public static final String KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT = "eutran_rsrp_hysteresis_db_int";
/**
* An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_RSRQ} measurement
@@ -3386,8 +3399,8 @@
* is set, the default value 2 is used.
* @hide
*/
- public static final String
- KEY_EUTRAN_RSSNR_HYSTERESIS_DB_INT = "eutran_rssnr_hysteresis_db_int";
+ public static final String KEY_EUTRAN_RSSNR_HYSTERESIS_DB_INT =
+ "eutran_rssnr_hysteresis_db_int";
/**
* Decides when clients try to bind to iwlan network service, which package name will
@@ -3436,6 +3449,7 @@
*/
public static final String KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING =
"carrier_qualified_networks_service_class_override_string";
+
/**
* A list of 4 WCDMA RSCP thresholds above which a signal level is considered POOR,
* MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
@@ -3511,7 +3525,7 @@
/**
* Specifies a carrier-defined {@link android.telecom.CallRedirectionService} which Telecom
- * will bind to for outgoing calls. An empty string indicates that no carrier-defined
+ * will bind to for outgoing calls. An empty string indicates that no carrier-defined
* {@link android.telecom.CallRedirectionService} is specified.
*/
public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
@@ -3811,7 +3825,7 @@
* This configuration allows the framework to use user data communication to detect Idle state,
* and this is used on the 5G icon.
*
- * There is a new way for for RRC state detection at Android 12. If
+ * There is a new way for RRC state detection at Android 12. If
* {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}(
* {@link TelephonyManager#CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED}) returns true,
* then framework can use PHYSICAL_CHANNEL_CONFIG for RRC state detection. Based on this
@@ -3967,8 +3981,7 @@
* FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
* Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 excluding '$'.
*/
- public static final String KEY_SMDP_SERVER_ADDRESS_STRING =
- "smdp_server_address_string";
+ public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
/**
* This timer value is used in the eSIM Exponential Backoff download retry algorithm.
@@ -4019,7 +4032,7 @@
public static final String KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL =
"opportunistic_esim_download_via_wifi_only_bool";
-/**
+ /**
* Controls RSRP threshold, in dBm, at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*
@@ -4113,6 +4126,7 @@
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG =
"opportunistic_network_ping_pong_time_long";
+
/**
* Controls back off time in milli seconds for switching back to
* opportunistic subscription. This time will be added to
@@ -4332,7 +4346,7 @@
* If opportunistic network is determined as out of service or below
* {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT} or
* {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE} within
- * {@link #KEY_5G_PING_PONG_TIME_LONG} of switching to opportunistic network,
+ * the time specified by this carrier config of switching to opportunistic network,
* it will be determined as ping pong situation by system app or 1st party app.
*
* @hide
@@ -4388,29 +4402,30 @@
public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
"enabled_4g_opportunistic_network_scan_bool";
- /**
- * Only relevant when the device supports opportunistic networks but does not support
- * simultaneuous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
- * goes out of service before switching the 5G capability back to primary stack. The idea of
- * waiting a few seconds is to minimize the calling of the expensive capability switching
- * operation in the case where CBRS goes back into service shortly after going out of it.
- *
- * @hide
- */
- public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
+ * goes out of service before switching the 5G capability back to primary stack. The idea of
+ * waiting a few seconds is to minimize the calling of the expensive capability switching
+ * operation in the case where CBRS goes back into service shortly after going out of it.
+ *
+ * @hide
+ */
+ public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
"time_to_switch_back_to_primary_if_opportunistic_oos_long";
- /**
- * Only relevant when the device supports opportunistic networks but does not support
- * simultaneuous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
- * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
- * 'ping-ponging' effect where device is constantly witching capability back and forth between
- * primary and opportunistic stack.
- *
- * @hide
- */
- public static final String KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG
- = "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
+ * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
+ * 'ping-ponging' effect where device is constantly witching capability back and forth between
+ * primary and opportunistic stack.
+ *
+ * @hide
+ */
+ public static final String
+ KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG =
+ "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
/**
* Indicates zero or more emergency number prefix(es), because some carrier requires
@@ -4537,8 +4552,7 @@
* @hide
*/
@SystemApi
- public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT =
- "gba_ua_security_protocol_int";
+ public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int";
/**
* An integer representing the cipher suite to be used when building the
@@ -4548,8 +4562,7 @@
* @hide
*/
@SystemApi
- public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT =
- "gba_ua_tls_cipher_suite_int";
+ public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int";
/**
* The data stall recovery timers array in milliseconds, each element is the delay before
@@ -4708,7 +4721,6 @@
*/
public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
-
/**
* Determine whether current lpp_mode used for E-911 needs to be kept persistently.
* {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
@@ -4826,8 +4838,8 @@
* The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
* @hide
*/
- public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
- + "es_supl_control_plane_support_int";
+ public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT =
+ KEY_PREFIX + "es_supl_control_plane_support_int";
/**
* A list of roaming PLMNs where SUPL ES mode does not support a control-plane mechanism to
@@ -4862,13 +4874,14 @@
}
}
- /**
- * An int array containing CDMA enhanced roaming indicator values for Home (non-roaming) network.
- * The default values come from 3GPP2 C.R1001 table 8.1-1.
- * Enhanced Roaming Indicator Number Assignments
- *
- * @hide
- */
+ /**
+ * An int array containing CDMA enhanced roaming indicator values for Home (non-roaming)
+ * network.
+ * The default values come from 3GPP2 C.R1001 table 8.1-1.
+ * Enhanced Roaming Indicator Number Assignments
+ *
+ * @hide
+ */
public static final String KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY =
"cdma_enhanced_roaming_indicator_for_home_network_int_array";
@@ -4880,7 +4893,7 @@
/**
* Indicates use 3GPP application to replace 3GPP2 application even if it's a CDMA/CDMA-LTE
- * phone, becasue some carriers's CSIM application is present but not supported.
+ * phone, because some carriers' CSIM application is present but not supported.
* @hide
*/
public static final String KEY_USE_USIM_BOOL = "use_usim_bool";
@@ -4943,8 +4956,7 @@
* {@see SubscriptionInfo#getUsageSetting}
*
*/
- public static final String KEY_CELLULAR_USAGE_SETTING_INT =
- "cellular_usage_setting_int";
+ public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
/**
* Data switch validation minimal gap time, in milliseconds.
@@ -5008,7 +5020,7 @@
* "Carrier Provisioning Info" or "Trigger Carrier Provisioning" button clicked.
*
* <p>
- * e.g, com.google.android.carrierPackageName/.CarrierReceiverName
+ * e.g., com.google.android.carrierPackageName/.CarrierReceiverName
*
* @hide
*/
@@ -5048,9 +5060,9 @@
* Capability Exchange (UCE). See RCC.71, section 3 for more information.
* <p>
* If this key's value is set to false, the procedure for RCS contact capability exchange
- * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and
- * {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be set to false to ensure
- * apps do not improperly think that capability exchange via SIP PUBLISH is enabled.
+ * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and this key must also be set
+ * to false to ensure apps do not improperly think that capability exchange via SIP PUBLISH
+ * is enabled.
* <p> The default value for this key is {@code false}.
*/
public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL =
@@ -5106,7 +5118,6 @@
public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL =
KEY_PREFIX + "enable_presence_capability_exchange_bool";
-
/**
* Flag indicating whether or not the carrier expects the RCS UCE service to periodically
* refresh the RCS capabilities cache of the user's contacts as well as request the
@@ -5240,7 +5251,7 @@
KEY_PREFIX + "sip_timer_j_millis_int";
/** Specifies the SIP Server default port. */
- public static final String KEY_SIP_SERVER_PORT_NUMBER_INT =
+ public static final String KEY_SIP_SERVER_PORT_NUMBER_INT =
KEY_PREFIX + "sip_server_port_number_int";
/**
@@ -5252,7 +5263,6 @@
/** @hide */
@IntDef({REQUEST_URI_FORMAT_TEL, REQUEST_URI_FORMAT_SIP})
-
public @interface RequestUriFormatType {}
/**
@@ -5299,7 +5309,6 @@
PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP,
PREFERRED_TRANSPORT_TLS
})
-
public @interface PreferredTransportType {}
/** Preferred Transport is always UDP. */
@@ -5380,7 +5389,6 @@
/** @hide */
@IntDef({IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5, IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1})
-
public @interface IpsecAuthenticationAlgorithmType {}
/** IPSec Authentication algorithm is HMAC-MD5. see Annex H of TS 33.203 */
@@ -5405,7 +5413,6 @@
IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC,
IPSEC_ENCRYPTION_ALGORITHM_AES_CBC
})
-
public @interface IpsecEncryptionAlgorithmType {}
/** IPSec Encryption algorithm is NULL. see Annex H of TS 33.203 */
@@ -5464,7 +5471,6 @@
GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR,
GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR
})
-
public @interface GeolocationPidfAllowedType {}
/**
@@ -5558,7 +5564,6 @@
NETWORK_TYPE_HOME,
NETWORK_TYPE_ROAMING
})
-
public @interface NetworkType {}
/** Indicates HOME Network. */
@@ -5575,7 +5580,6 @@
E911_RTCP_INACTIVITY_ON_CONNECTED,
E911_RTP_INACTIVITY_ON_CONNECTED
})
-
public @interface MediaInactivityReason {}
/** RTCP inactivity occurred when call is on HOLD. */
@@ -5621,7 +5625,7 @@
* <li>{@link #KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY}</li>
* </ul>
* <p> The values are defined in
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech}
*
* changing mmtel_requires_provisioning_bundle requires changes to
* carrier_volte_provisioning_required_bool and vice versa
@@ -5635,10 +5639,10 @@
* is supported.
* @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE
* <p>Possible values are,
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY =
KEY_PREFIX + "capability_type_voice_int_array";
@@ -5648,10 +5652,10 @@
* is supported.
* @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO
* <p>Possible values are,
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY =
KEY_PREFIX + "capability_type_video_int_array";
@@ -5661,10 +5665,10 @@
* supplementary services. (IR.92) is supported.
* @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT
* <p>Possible values are,
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY =
KEY_PREFIX + "capability_type_ut_int_array";
@@ -5673,10 +5677,10 @@
* List of different RAT technologies on which Provisioning for SMS (IR.92) is supported.
* @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS
* <p>Possible values are,
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY =
KEY_PREFIX + "capability_type_sms_int_array";
@@ -5686,10 +5690,10 @@
* (section 2.4 of RCC.20) is supported.
* @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER
* <p>Possible values are,
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY =
KEY_PREFIX + "capability_type_call_composer_int_array";
@@ -5705,7 +5709,7 @@
* <li>{@link #KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY}</li>
* </ul>
* <p> The values are defined in
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech}
*/
public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE =
KEY_PREFIX + "rcs_requires_provisioning_bundle";
@@ -5716,10 +5720,10 @@
* If not set, this RcsFeature should not service capability requests.
* @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE
* <p>Possible values are,
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY =
KEY_PREFIX + "capability_type_options_uce_int_array";
@@ -5731,10 +5735,10 @@
* requests using presence.
* @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE
* <p>Possible values are,
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY =
KEY_PREFIX + "capability_type_presence_uce_int_array";
@@ -5747,7 +5751,7 @@
defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
defaults.putStringArray(KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY,
- new String[] {});
+ new String[0]);
defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, false);
@@ -5872,7 +5876,7 @@
* <p>If {@code false}: hard disabled.
* If {@code true}: then depends on availability, etc.
*/
- public static final String KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL =
+ public static final String KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL =
KEY_PREFIX + "carrier_volte_roaming_available_bool";
/**
@@ -5883,7 +5887,7 @@
* will be sent in the dialed string in the SIP:INVITE.
* If {@code false}, *67 and *82 will be removed.
*/
- public static final String KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL =
+ public static final String KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL =
KEY_PREFIX + "include_caller_id_service_codes_in_sip_invite_bool";
/**
@@ -5927,7 +5931,6 @@
SESSION_REFRESHER_TYPE_UAC,
SESSION_REFRESHER_TYPE_UAS
})
-
public @interface SessionRefresherType {}
/**
@@ -5969,14 +5972,12 @@
public static final String KEY_SESSION_REFRESHER_TYPE_INT =
KEY_PREFIX + "session_refresher_type_int";
-
/** @hide */
@IntDef({
SESSION_PRIVACY_TYPE_HEADER,
SESSION_PRIVACY_TYPE_NONE,
SESSION_PRIVACY_TYPE_ID
})
-
public @interface SessionPrivacyType {}
/**
@@ -6014,7 +6015,7 @@
* If {@code true}, SIP 18x responses (other than SIP 183 response)
* are sent reliably.
*/
- public static final String KEY_PRACK_SUPPORTED_FOR_18X_BOOL =
+ public static final String KEY_PRACK_SUPPORTED_FOR_18X_BOOL =
KEY_PREFIX + "prack_supported_for_18x_bool";
/** @hide */
@@ -6022,7 +6023,6 @@
CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG,
CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG
})
-
public @interface ConferenceSubscribeType {}
/**
@@ -6043,7 +6043,7 @@
/**
* This is used to specify whether the SIP SUBSCRIBE to conference state events,
- * is sent in or out of the SIP INVITE dialog between the UE and the
+ * is sent in or out of the SIP INVITE dialog between the UE and the
* conference server.
*
* <p>Reference: IR.92 Section 2.3.3.
@@ -6068,7 +6068,7 @@
*
* <p>Reference: 3GPP TS 24.229
*/
- public static final String KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL =
+ public static final String KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL =
KEY_PREFIX + "voice_qos_precondition_supported_bool";
/**
@@ -6076,7 +6076,7 @@
*
* <p>If {@code true}: voice packets can be sent on default bearer. {@code false} otherwise.
*/
- public static final String KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
+ public static final String KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
KEY_PREFIX + "voice_on_default_bearer_supported_bool";
/**
@@ -6098,7 +6098,6 @@
PREALERTING_SRVCC_SUPPORT,
MIDCALL_SRVCC_SUPPORT
})
-
public @interface SrvccType {}
/**
@@ -6196,7 +6195,6 @@
SESSION_REFRESH_METHOD_INVITE,
SESSION_REFRESH_METHOD_UPDATE_PREFERRED
})
-
public @interface SessionRefreshMethod {}
/**
@@ -6231,7 +6229,7 @@
* determination of the originating party identity in OIP.
* {@code false} otherwise.
*/
- public static final String KEY_OIP_SOURCE_FROM_HEADER_BOOL =
+ public static final String KEY_OIP_SOURCE_FROM_HEADER_BOOL =
KEY_PREFIX + "oip_source_from_header_bool";
/**
@@ -6319,7 +6317,7 @@
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_EVS_PAYLOAD_TYPE_INT_ARRAY =
+ public static final String KEY_EVS_PAYLOAD_TYPE_INT_ARRAY =
KEY_PREFIX + "evs_payload_type_int_array";
/**
@@ -6328,7 +6326,7 @@
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY =
+ public static final String KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY =
KEY_PREFIX + "amrwb_payload_type_int_array";
/**
@@ -6337,7 +6335,7 @@
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY =
+ public static final String KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY =
KEY_PREFIX + "amrnb_payload_type_int_array";
/**
@@ -6346,7 +6344,7 @@
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY =
+ public static final String KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY =
KEY_PREFIX + "dtmfwb_payload_type_int_array";
/**
@@ -6355,7 +6353,7 @@
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY =
+ public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY =
KEY_PREFIX + "dtmfnb_payload_type_int_array";
/**
@@ -6372,7 +6370,6 @@
public static final String KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT =
KEY_PREFIX + "rtp_packet_loss_rate_threshold_int";
-
/**
* This indicates the threshold for RTP jitter value in milliseconds (RFC3550). If measured
* jitter value crosses this, a callback with {@link MediaQualityStatus} will be invoked
@@ -6401,13 +6398,11 @@
public static final String KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG =
KEY_PREFIX + "rtp_inactivity_time_threshold_millis_long";
-
/** @hide */
@IntDef({
BANDWIDTH_EFFICIENT,
OCTET_ALIGNED
})
-
public @interface AmrPayloadFormat {}
/** AMR NB/WB Payload format is bandwidth-efficient. */
@@ -6428,7 +6423,7 @@
*
* <p>Reference: RFC 4867 Section 8.1.
*/
- public static final String KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT =
+ public static final String KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT =
KEY_PREFIX + "amr_codec_attribute_payload_format_int";
/**
@@ -6443,7 +6438,7 @@
* <p>Possible values are subset of,
* [0,1,2,3,4,5,6,7,8] - AMRWB with the modes representing nine speech codec modes
* with bit rates of 6.6, 8.85, 12.65, 14.25, 15.85, 18.25, 19.85, 23.05, 23.85 kbps.
- * [0,1,2,3,4,5,6,7] - AMRNB with the modes representing eight speech codec modes
+ * [0,1,2,3,4,5,6,7] - AMRNB with the modes representing eight speech codec modes
* with bit rates of 4.75, 5.15, 5.90, 6.70, 7.40, 7.95, 10.2, 12.2 kbps.
*
* <p>If value is not specified, then it means device supports all
@@ -6451,7 +6446,7 @@
*
* <p>Reference: RFC 4867 Section 8.1, 3GPP 26.445 A.3.1
*/
- public static final String KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY =
+ public static final String KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY =
KEY_PREFIX + "amr_codec_attribute_modeset_int_array";
/**
@@ -6501,7 +6496,6 @@
EVS_OPERATIONAL_MODE_PRIMARY,
EVS_OPERATIONAL_MODE_AMRWB_IO
})
-
public @interface EvsOperationalMode {}
/** Indicates the EVS primary mode. 3GPP 26.445 Section 3.1 */
@@ -6536,7 +6530,6 @@
EVS_ENCODED_BW_TYPE_WB_SWB,
EVS_ENCODED_BW_TYPE_WB_SWB_FB
})
-
public @interface EvsEncodedBwType {}
/**
@@ -6612,7 +6605,7 @@
*
* <p>Reference: 3GPP 26.441 Table 1.
*/
- public static final String KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT =
+ public static final String KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT =
KEY_PREFIX + "evs_codec_attribute_bandwidth_int";
/** @hide */
@@ -6630,7 +6623,6 @@
EVS_PRIMARY_MODE_BITRATE_96_0_KBPS,
EVS_PRIMARY_MODE_BITRATE_128_0_KBPS
})
-
public @interface EvsPrimaryModeBitRate {}
/** EVS primary mode with bitrate 5.9 kbps */
@@ -6696,14 +6688,14 @@
*
* <p>Reference: 3GPP 26.445 Section A.3.1
*/
- public static final String KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY =
+ public static final String KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY =
KEY_PREFIX + "evs_codec_attribute_bitrate_int_array";
/**
* Specifies the Channel aware mode (ch-aw-recv) for the receive direction.
* This is applicable for EVS codec.
*
- * <p>Permissible values are -1, 0, 2, 3, 5, and 7.
+ * <p> Permissible values are -1, 0, 2, 3, 5, and 7.
* If this key is not specified, then the behavior is same as value 0
* (channel aware mode disabled).
* <p> If this key is configured, then device is expected to send
@@ -6711,7 +6703,7 @@
*
* <p>Reference: 3GPP TS 26.445 section 4.4.5, 3GPP 26.445 Section A.3.1
*/
- public static final String KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT =
+ public static final String KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT =
KEY_PREFIX + "evs_codec_attribute_ch_aw_recv_int";
/**
@@ -6732,7 +6724,7 @@
*
* <p>Reference: 3GPP 26.445 Section A.3.1.
*/
- public static final String KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT =
+ public static final String KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT =
KEY_PREFIX + "evs_codec_attribute_hf_only_int";
/**
@@ -6748,7 +6740,7 @@
* will apply.
* <p>Reference: 3GPP TS 26.445 Section A.3.1.
*/
- public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL =
+ public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL =
KEY_PREFIX + "evs_codec_attribute_dtx_bool";
/**
@@ -6773,7 +6765,7 @@
*
* <p>Reference: RFC 3551
*/
- public static final String KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT =
+ public static final String KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT =
KEY_PREFIX + "evs_codec_attribute_channels_int";
/**
@@ -6786,7 +6778,7 @@
*
* <p>Reference: 3GPP 26.445 Section A.3.1, 3GPP 26.114 Table 6.2a
*/
- public static final String KEY_EVS_CODEC_ATTRIBUTE_CMR_INT =
+ public static final String KEY_EVS_CODEC_ATTRIBUTE_CMR_INT =
KEY_PREFIX + "codec_attribute_cmr_int";
/**
@@ -6800,7 +6792,7 @@
*
* <p>Reference: RFC 4867 Section 8.1.
*/
- public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT =
+ public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT =
KEY_PREFIX + "codec_attribute_mode_change_period_int";
/**
@@ -6814,7 +6806,7 @@
*
* <p>Reference: RFC 4867 Section 8.1.
*/
- public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT =
+ public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT =
KEY_PREFIX + "codec_attribute_mode_change_capability_int";
/**
@@ -6822,7 +6814,7 @@
* This attribute is applicable for EVS codec in AMR-WB IO mode
* and AMR-WB.
*
- * <p>Possible values are 0, 1. If value is 1, then the sender should only
+ * <p>Possible values are 0, 1. If value is 1, then the sender should only
* perform mode changes to the neighboring modes in the active codec mode set.
* If value is 0, then mode changes between any two modes
* in the active codec mode set is allowed.
@@ -6831,7 +6823,7 @@
*
* <p>Reference: RFC 4867 Section 8.1.
*/
- public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT =
+ public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT =
KEY_PREFIX + "codec_attribute_mode_change_neighbor_int";
/**
@@ -6995,7 +6987,7 @@
* <p>If {@code true}: SMS over IMS support available.
* {@code false}: otherwise.
*/
- public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL =
+ public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL =
KEY_PREFIX + "sms_over_ims_supported_bool";
/**
@@ -7005,7 +6997,7 @@
* <p>If {@code true}: allow SMS CSFB in case of SMS over PS failure.
* {@code false} otherwise.
*/
- public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL =
+ public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL =
KEY_PREFIX + "sms_csfb_retry_on_failure_bool";
/** @hide */
@@ -7013,7 +7005,6 @@
SMS_FORMAT_3GPP,
SMS_FORMAT_3GPP2
})
-
public @interface SmsFormat {}
/** SMS format is 3GPP. */
@@ -7192,7 +7183,7 @@
* <p>If {@code true}: text media can be sent on default bearer.
* {@code false} otherwise.
*/
- public static final String KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
+ public static final String KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
KEY_PREFIX + "text_on_default_bearer_supported_bool";
/**
@@ -7202,7 +7193,7 @@
* {@code false} otherwise.
* <p>Reference: 3GPP TS 24.229
*/
- public static final String KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL =
+ public static final String KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL =
KEY_PREFIX + "text_qos_precondition_supported_bool";
/**
@@ -7252,14 +7243,14 @@
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_T140_PAYLOAD_TYPE_INT =
+ public static final String KEY_T140_PAYLOAD_TYPE_INT =
KEY_PREFIX + "t140_payload_type_int";
/** Integer representing payload type for RED/redundancy codec.
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_RED_PAYLOAD_TYPE_INT =
+ public static final String KEY_RED_PAYLOAD_TYPE_INT =
KEY_PREFIX + "red_payload_type_int";
private static PersistableBundle getDefaults() {
@@ -7308,7 +7299,7 @@
* <p>If {@code true}: Allow UE to retry emergency call on
* IMS PDN if emergency PDN setup failed.{@code false} otherwise.
*/
- public static final String KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL =
+ public static final String KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL =
KEY_PREFIX + "retry_emergency_on_ims_pdn_bool";
/**
@@ -7318,7 +7309,7 @@
* <p>If {@code true}: Enter ECBM mode after E911 call is ended.
* {@code false} otherwise.
*/
- public static final String KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL =
+ public static final String KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL =
KEY_PREFIX + "emergency_callback_mode_supported_bool";
/**
@@ -7330,7 +7321,7 @@
*
* <p>Reference: 3GPP TS 24.229
*/
- public static final String KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL =
+ public static final String KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL =
KEY_PREFIX + "emergency_qos_precondition_supported_bool";
/**
@@ -7737,7 +7728,7 @@
defaults.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, false);
defaults.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, false);
defaults.putStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY,
- new String[] {});
+ new String[0]);
return defaults;
}
@@ -7758,7 +7749,7 @@
* <p>If {@code true}: video media can be sent on default bearer.
* {@code false} otherwise.
*/
- public static final String KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
+ public static final String KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
KEY_PREFIX + "video_on_default_bearer_supported_bool";
/**
@@ -7824,7 +7815,7 @@
* {@code false} otherwise.
* <p>Reference: 3GPP TS 24.229
*/
- public static final String KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL =
+ public static final String KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL =
KEY_PREFIX + "video_qos_precondition_supported_bool";
/**
@@ -7849,7 +7840,7 @@
* <p>Payload type is an integer in dynamic payload type range 96-127
* as per RFC RFC 3551 Section 6.
*/
- public static final String KEY_H264_PAYLOAD_TYPE_INT_ARRAY =
+ public static final String KEY_H264_PAYLOAD_TYPE_INT_ARRAY =
KEY_PREFIX + "h264_payload_type_int_array";
/**
@@ -7891,7 +7882,7 @@
*
* <p>Reference: RFC 6184 Section 5.4
*/
- public static final String KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT =
+ public static final String KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT =
KEY_PREFIX + "video_codec_attribute_packetization_mode_int";
/**
@@ -7905,7 +7896,7 @@
* <UL>
* <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2
*/
- public static final String KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT =
+ public static final String KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT =
KEY_PREFIX + "video_codec_attribute_frame_rate_int";
/**
@@ -7924,7 +7915,7 @@
* <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2
*
*/
- public static final String KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY =
+ public static final String KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY =
KEY_PREFIX + "video_codec_attribute_resolution_int_array";
/**
@@ -7937,7 +7928,7 @@
*
* <p>Reference: RFC 6184 Section 8.1, ITU-T Recommendation H.264
*/
- public static final String KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING =
+ public static final String KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING =
KEY_PREFIX + "h264_video_codec_attribute_profile_level_id_string";
private static PersistableBundle getDefaults() {
@@ -8005,7 +7996,7 @@
* List of MDNs for which Geo-location PIDF XML with country info
* needs to included for normal calls involving short code.
*/
- public static final String KEY_PIDF_SHORT_CODE_STRING_ARRAY =
+ public static final String KEY_PIDF_SHORT_CODE_STRING_ARRAY =
KEY_PREFIX + "pidf_short_code_string_array";
/**
@@ -8015,15 +8006,14 @@
* <p>If {@code false}: E911 call uses IMS PDN for E911 call over VoWiFi.
* If {@code true}: E911 call uses Emergency PDN for E911 call over VoWiFi.
*/
- public static final String KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL =
+ public static final String KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL =
KEY_PREFIX + "emergency_call_over_emergency_pdn_bool";
-
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, false);
- defaults.putStringArray(KEY_PIDF_SHORT_CODE_STRING_ARRAY, new String[] {});
+ defaults.putStringArray(KEY_PIDF_SHORT_CODE_STRING_ARRAY, new String[0]);
return defaults;
}
@@ -8070,7 +8060,7 @@
* If XCAP over UT fails, return error.
* if {@code true}, Use CSFB if XCAP over UT fails.
*/
- public static final String KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL =
+ public static final String KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL =
KEY_PREFIX + "use_csfb_on_xcap_over_ut_failure_bool";
/**
@@ -8082,7 +8072,7 @@
*
* Reference: IR.92 Section 5.5.1
*/
- public static final String KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL =
+ public static final String KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL =
KEY_PREFIX + "ut_supported_when_ps_data_off_bool";
/**
@@ -8092,7 +8082,7 @@
* <p>If {@code true}: Support Available.{@code false}: Otherwise.
* Reference: 3GPP 24.390.
*/
- public static final String KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL =
+ public static final String KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL =
KEY_PREFIX + "network_initiated_ussd_over_ims_supported_bool";
/**
@@ -8176,7 +8166,6 @@
SUPPLEMENTARY_SERVICE_CB_ACR,
SUPPLEMENTARY_SERVICE_CB_BIL
})
-
public @interface SsType {}
/** Communication Waiting (CW) support as per 3GPP 24.615. */
@@ -8415,7 +8404,6 @@
CALL_WAITING_SYNC_FIRST_CHANGE,
CALL_WAITING_SYNC_IMS_ONLY
})
-
public @interface CwSyncType {}
/**
@@ -8540,9 +8528,7 @@
SUPPLEMENTARY_SERVICE_CB_ACR,
SUPPLEMENTARY_SERVICE_CB_BIL
});
- defaults.putIntArray(
- KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY,
- new int[] {});
+ defaults.putIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY, new int[0]);
defaults.putIntArray(
KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY,
@@ -8726,7 +8712,6 @@
public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array";
-
/**
* List of supported key sizes for AES Counter (CTR) encryption mode of IKE session.
* Possible values -
@@ -8861,7 +8846,7 @@
public static final int EPDG_ADDRESS_PCO = 2;
/** Use cellular location to chose epdg server */
public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
- /* Use Visited Country FQDN rule*/
+ /** Use Visited Country FQDN rule*/
public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4;
/** @hide */
@@ -8999,7 +8984,7 @@
defaults.putIntArray(
KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
- defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[] {});
+ defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[0]);
defaults.putInt(KEY_IKE_LOCAL_ID_TYPE_INT, ID_TYPE_RFC822_ADDR);
defaults.putInt(KEY_IKE_REMOTE_ID_TYPE_INT, ID_TYPE_FQDN);
defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
@@ -9022,8 +9007,7 @@
* level outside these boundaries is considered invalid.
* @hide
*/
- public static final String KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY =
- "gsm_rssi_thresholds_int_array";
+ public static final String KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY = "gsm_rssi_thresholds_int_array";
/**
* An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_RSSI} measurement
@@ -9041,8 +9025,7 @@
* See Wireless Priority Service from https://www.fcc.gov/general/wireless-priority-service-wps
* @hide
*/
- public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL =
- "support_wps_over_ims_bool";
+ public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL = "support_wps_over_ims_bool";
/**
* The two digital number pattern of MMI code which is defined by carrier.
@@ -9090,8 +9073,7 @@
* When true, forwarded number is shown.
* When false, forwarded number is not shown.
*/
- public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL =
- "show_forwarded_number_bool";
+ public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool";
/**
* The list of originating address of missed incoming call SMS. If the SMS has originator
@@ -9103,7 +9085,6 @@
public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
"missed_incoming_call_sms_originator_string_array";
-
/**
* String array of Apn Type configurations.
* The entries should be of form "APN_TYPE_NAME:priority".
@@ -9251,8 +9232,7 @@
*
* @hide
*/
- public static final String KEY_DEFAULT_RTT_MODE_INT =
- "default_rtt_mode_int";
+ public static final String KEY_DEFAULT_RTT_MODE_INT = "default_rtt_mode_int";
/**
* Indicates whether RTT is supported while roaming.
@@ -9278,10 +9258,9 @@
* seamlessly after an unattended reboot.
*
* The device configuration value {@code config_allow_pin_storage_for_unattended_reboot}
- * ultimately controls whether this carrier configuration option is used. Where
- * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of the
- * {@link #KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL} carrier configuration option is
- * ignored.
+ * ultimately controls whether this carrier configuration option is used. Where
+ * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of this
+ * carrier configuration is ignored.
*
* @hide
*/
@@ -9559,7 +9538,7 @@
"iwlan_handover_policy_string_array";
/** The default value for every variable. */
- private final static PersistableBundle sDefaults;
+ private static final PersistableBundle sDefaults;
static {
sDefaults = new PersistableBundle();
@@ -9669,17 +9648,17 @@
sDefaults.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, true);
sDefaults.putBoolean(KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL, false);
- sDefaults.putIntArray(KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY, new int[]{});
+ sDefaults.putIntArray(KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY, new int[0]);
sDefaults.putInt(KEY_VOLTE_REPLACEMENT_RAT_INT, 0);
sDefaults.putString(KEY_DEFAULT_SIM_CALL_MANAGER_STRING, "");
sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
sDefaults.putInt(KEY_VVM_PORT_NUMBER_INT, 0);
sDefaults.putString(KEY_VVM_TYPE_STRING, "");
sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false);
- sDefaults.putString(KEY_VVM_CLIENT_PREFIX_STRING,"//VVM");
- sDefaults.putBoolean(KEY_VVM_SSL_ENABLED_BOOL,false);
+ sDefaults.putString(KEY_VVM_CLIENT_PREFIX_STRING, "//VVM");
+ sDefaults.putBoolean(KEY_VVM_SSL_ENABLED_BOOL, false);
sDefaults.putStringArray(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY, null);
- sDefaults.putBoolean(KEY_VVM_LEGACY_MODE_ENABLED_BOOL,false);
+ sDefaults.putBoolean(KEY_VVM_LEGACY_MODE_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_VVM_PREFETCH_BOOL, true);
sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null);
@@ -9836,7 +9815,6 @@
sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL, false);
-
// Default carrier app configurations
sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY,
new String[]{
@@ -9850,9 +9828,10 @@
//6: CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
//8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
});
- sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE, new String[] {
- String.valueOf(false) + ": 7", //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
- String.valueOf(true) + ": 8" //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+ sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE,
+ new String[] {
+ false + ": 7", //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+ true + ": 8" //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
});
sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
@@ -9866,9 +9845,9 @@
// Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
// {LTE, LTE_CA}
- // Order is important - lowest precidence first
+ // Order is important - lowest precedence first
sDefaults.putStringArray(KEY_RATCHET_RAT_FAMILIES,
- new String[]{"1,2","7,8,12","3,11,9,10,15","14,19"});
+ new String[]{"1,2", "7,8,12", "3,11,9,10,15", "14,19"});
sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, false);
sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
@@ -9899,8 +9878,7 @@
sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
- sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
- null);
+ sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
@@ -9939,6 +9917,7 @@
sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
sDefaults.putBoolean(KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL, false);
+ sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, true);
sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
@@ -9977,27 +9956,27 @@
sDefaults.putIntArray(KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
// Boundaries: [-120 dBm, -25 dBm]
new int[] {
- -115, /* SIGNAL_STRENGTH_POOR */
+ -115, /* SIGNAL_STRENGTH_POOR */
-105, /* SIGNAL_STRENGTH_MODERATE */
- -95, /* SIGNAL_STRENGTH_GOOD */
- -85 /* SIGNAL_STRENGTH_GREAT */
+ -95, /* SIGNAL_STRENGTH_GOOD */
+ -85 /* SIGNAL_STRENGTH_GREAT */
});
// TODO(b/249896055): On enabling ECNO measurement part for Signal Bar level indication
- // system functionality,below values to be rechecked.
+ // system functionality, below values to be rechecked.
sDefaults.putIntArray(KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY,
// Boundaries: [-24 dBm, 1 dBm]
new int[] {
-24, /* SIGNAL_STRENGTH_POOR */
-14, /* SIGNAL_STRENGTH_MODERATE */
- -6, /* SIGNAL_STRENGTH_GOOD */
- 1 /* SIGNAL_STRENGTH_GREAT */
+ -6, /* SIGNAL_STRENGTH_GOOD */
+ 1 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
// Boundaries: [-140 dB, -44 dB]
new int[] {
-110, /* SIGNAL_STRENGTH_POOR */
- -90, /* SIGNAL_STRENGTH_MODERATE */
- -80, /* SIGNAL_STRENGTH_GOOD */
+ -90, /* SIGNAL_STRENGTH_MODERATE */
+ -80, /* SIGNAL_STRENGTH_GOOD */
-65, /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
@@ -10005,14 +9984,14 @@
new int[] {
-31, /* SIGNAL_STRENGTH_POOR */
-19, /* SIGNAL_STRENGTH_MODERATE */
- -7, /* SIGNAL_STRENGTH_GOOD */
- 6 /* SIGNAL_STRENGTH_GREAT */
+ -7, /* SIGNAL_STRENGTH_GOOD */
+ 6 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
// Boundaries: [-23 dB, 40 dB]
new int[] {
-5, /* SIGNAL_STRENGTH_POOR */
- 5, /* SIGNAL_STRENGTH_MODERATE */
+ 5, /* SIGNAL_STRENGTH_MODERATE */
15, /* SIGNAL_STRENGTH_GOOD */
30 /* SIGNAL_STRENGTH_GREAT */
});
@@ -10106,8 +10085,7 @@
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
sDefaults.putLong(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000L);
- sDefaults.putLong(
- KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
+ sDefaults.putLong(KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
120000L);
sDefaults.putAll(ImsServiceEntitlement.getDefaults());
sDefaults.putAll(Gps.getDefaults());
@@ -10129,7 +10107,7 @@
new int[] {
-107, /* SIGNAL_STRENGTH_POOR */
-103, /* SIGNAL_STRENGTH_MODERATE */
- -97, /* SIGNAL_STRENGTH_GOOD */
+ -97, /* SIGNAL_STRENGTH_GOOD */
-89, /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
@@ -10221,7 +10199,7 @@
sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_VONR_ON_BY_DEFAULT_BOOL, true);
- sDefaults.putIntArray(KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY, new int[] {});
+ sDefaults.putIntArray(KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY, new int[0]);
sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG,
TimeUnit.MINUTES.toMillis(30));
sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
@@ -10290,7 +10268,6 @@
public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL =
KEY_PREFIX + "avoid_5ghz_wifi_direct_for_laa_bool";
-
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0);
@@ -10337,8 +10314,7 @@
return loader.getConfigForSubIdWithFeature(subId, mContext.getOpPackageName(),
mContext.getAttributionTag());
} catch (RemoteException ex) {
- Rlog.e(TAG, "Error getting config for subId " + subId + ": "
- + ex.toString());
+ Rlog.e(TAG, "Error getting config for subId " + subId + ": " + ex);
}
return null;
}
@@ -10364,7 +10340,7 @@
* {@link TelephonyManager#hasCarrierPrivileges()}).
*
* @param subId The subscription ID on which the carrier config should be retrieved.
- * @param keys The carrier config keys to retrieve values.
+ * @param keys The carrier config keys to retrieve values.
* @return A {@link PersistableBundle} with key/value mapping for the specified configuration
* on success, or an empty (but never null) bundle on failure (for example, when the calling app
* has no permission).
@@ -10455,8 +10431,7 @@
}
loader.overrideConfig(subscriptionId, overrideValues, persistent);
} catch (RemoteException ex) {
- Rlog.e(TAG, "Error setting config for subId " + subscriptionId + ": "
- + ex.toString());
+ Rlog.e(TAG, "Error setting config for subId " + subscriptionId + ": " + ex);
}
}
@@ -10569,7 +10544,7 @@
}
loader.notifyConfigChangedForSubId(subId);
} catch (RemoteException ex) {
- Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex.toString());
+ Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex);
}
}
@@ -10593,7 +10568,7 @@
}
loader.updateConfigForPhoneId(phoneId, simState);
} catch (RemoteException ex) {
- Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex.toString());
+ Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex);
}
}
@@ -10615,8 +10590,7 @@
}
return loader.getDefaultCarrierServicePackageName();
} catch (RemoteException ex) {
- Rlog.e(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null"
- + ex.toString());
+ Rlog.e(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null" + ex);
ex.rethrowAsRuntimeException();
}
return "";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 37d1e94..83b9098 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3242,17 +3242,17 @@
case NETWORK_TYPE_CDMA:
return "CDMA";
case NETWORK_TYPE_EVDO_0:
- return "EVDO-0";
+ return "CDMA - EvDo rev. 0";
case NETWORK_TYPE_EVDO_A:
- return "EVDO-A";
+ return "CDMA - EvDo rev. A";
case NETWORK_TYPE_EVDO_B:
- return "EVDO-B";
+ return "CDMA - EvDo rev. B";
case NETWORK_TYPE_1xRTT:
- return "1xRTT";
+ return "CDMA - 1xRTT";
case NETWORK_TYPE_LTE:
return "LTE";
case NETWORK_TYPE_EHRPD:
- return "eHRPD";
+ return "CDMA - eHRPD";
case NETWORK_TYPE_IDEN:
return "iDEN";
case NETWORK_TYPE_HSPAP:
@@ -3260,7 +3260,7 @@
case NETWORK_TYPE_GSM:
return "GSM";
case NETWORK_TYPE_TD_SCDMA:
- return "TD-SCDMA";
+ return "TD_SCDMA";
case NETWORK_TYPE_IWLAN:
return "IWLAN";
case NETWORK_TYPE_LTE_CA:
@@ -9574,10 +9574,10 @@
*/
public static boolean isValidAllowedNetworkTypesReason(@AllowedNetworkTypesReason int reason) {
switch (reason) {
- case ALLOWED_NETWORK_TYPES_REASON_USER:
- case ALLOWED_NETWORK_TYPES_REASON_POWER:
- case ALLOWED_NETWORK_TYPES_REASON_CARRIER:
- case ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
case ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS:
return true;
}
@@ -18118,44 +18118,4 @@
return "UNKNOWN(" + state + ")";
}
}
-
- /**
- * Convert the allowed network types reason to string.
- *
- * @param reason The allowed network types reason.
- * @return The converted string.
- *
- * @hide
- */
- @NonNull
- public static String allowedNetworkTypesReasonToString(@AllowedNetworkTypesReason int reason) {
- switch (reason) {
- case ALLOWED_NETWORK_TYPES_REASON_USER: return "user";
- case ALLOWED_NETWORK_TYPES_REASON_POWER: return "power";
- case ALLOWED_NETWORK_TYPES_REASON_CARRIER: return "carrier";
- case ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: return "enable_2g";
- case ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS: return "user_restrictions";
- default: return "unknown(" + reason + ")";
- }
- }
-
- /**
- * Convert the allowed network types reason from string.
- *
- * @param reason The reason in string format.
- * @return The allowed network types reason.
- *
- * @hide
- */
- @AllowedNetworkTypesReason
- public static int allowedNetworkTypesReasonFromString(@NonNull String reason) {
- switch (reason) {
- case "user": return ALLOWED_NETWORK_TYPES_REASON_USER;
- case "power": return ALLOWED_NETWORK_TYPES_REASON_POWER;
- case "carrier": return ALLOWED_NETWORK_TYPES_REASON_CARRIER;
- case "enable_2g": return ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G;
- case "user_restrictions": return ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS;
- default: return -1;
- }
- }
}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a2d2019..cdb7d7c 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1570,8 +1570,8 @@
/**
* Returns whether the passing portIndex is available.
- * A port is available if it is active without enabled profile on it or
- * calling app has carrier privilege over the profile installed on the selected port.
+ * A port is available if it is active without an enabled profile on it or calling app can
+ * activate a new profile on the selected port without any user interaction.
* Always returns false if the cardId is a physical card.
*
* @param portIndex is an enumeration of the ports available on the UICC.
diff --git a/telephony/java/android/telephony/ims/MediaQualityStatus.java b/telephony/java/android/telephony/ims/MediaQualityStatus.java
index 62c289a..5038aac 100644
--- a/telephony/java/android/telephony/ims/MediaQualityStatus.java
+++ b/telephony/java/android/telephony/ims/MediaQualityStatus.java
@@ -226,7 +226,7 @@
public Builder(
@NonNull String imsCallSessionId,
@MediaSessionType int mediaSessionType,
- int transportType) {
+ @TransportType int transportType) {
mImsCallSessionId = imsCallSessionId;
mMediaSessionType = mediaSessionType;
mTransportType = transportType;
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 4070bed..28c2d59 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -115,6 +115,10 @@
@Deprecated
default void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+ onPublishUpdated(new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+ .setSipResponseCode(reasonCode, reasonPhrase)
+ .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+ .build());
}
/**
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 3e1c9ef..66442a6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -27,6 +27,7 @@
import android.telephony.ims.SipDetails;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -205,6 +206,12 @@
* when the Telephony stack has crashed.
*/
default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+ if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+ } else {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+ details.getReasonHeaderCause(), details.getReasonHeaderText());
+ }
}
}
@@ -338,7 +345,14 @@
* {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
* This may also happen in rare cases when the Telephony stack has crashed.
*/
- default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {};
+ default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+ if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+ } else {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+ details.getReasonHeaderCause(), details.getReasonHeaderText());
+ }
+ };
/**
* Notify the framework of the latest XML PIDF documents included in the network response
diff --git a/tests/CanvasCompare/Android.bp b/tests/CanvasCompare/Android.bp
deleted file mode 100644
index 9883115..0000000
--- a/tests/CanvasCompare/Android.bp
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 {
- // See: http://go/android-license-faq
- default_applicable_licenses: [
- "frameworks_base_license",
- ],
-}
-
-android_test {
- name: "CanvasCompare",
- srcs: [
- "src/**/*.java",
- ":CanvasCompare-rscript{CanvasCompare.srcjar}",
- ],
- resource_zips: [
- ":CanvasCompare-rscript{CanvasCompare.res.zip}",
- ],
- platform_apis: true,
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
- static_libs: ["junit"],
-}
-
-genrule {
- name: "CanvasCompare-rscript",
- srcs: [
- "src/**/*.rscript",
- ":rs_script_api",
- ":rs_clang_headers",
- ],
- tools: [
- "llvm-rs-cc",
- "soong_zip",
- ],
- out: [
- "CanvasCompare.srcjar",
- "CanvasCompare.res.zip",
- ],
- cmd: "for f in $(locations src/**/*.rscript); do " +
- " $(location llvm-rs-cc) -o $(genDir)/res/raw -p $(genDir)/src " +
- " -I $$(dirname $$(echo $(locations :rs_script_api) | awk '{ print $$1 }')) " +
- " -I $$(dirname $$(echo $(locations :rs_clang_headers) | awk '{ print $$1 }')) $${f}; " +
- "done && " +
- "$(location soong_zip) -srcjar -o $(location CanvasCompare.srcjar) -C $(genDir)/src -D $(genDir)/src &&" +
- "$(location soong_zip) -o $(location CanvasCompare.res.zip) -C $(genDir)/res -D $(genDir)/res",
-}
diff --git a/tests/CanvasCompare/AndroidManifest.xml b/tests/CanvasCompare/AndroidManifest.xml
deleted file mode 100644
index 2734e7f..0000000
--- a/tests/CanvasCompare/AndroidManifest.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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.test.hwuicompare">
-
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
- <application android:label="@string/app_name"
- android:theme="@android:style/Theme.Holo.Light.NoActionBar">
- <activity android:name="AutomaticActivity"
- android:label="CanvasAutoCompare"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity android:name="ManualActivity"
- android:label="CanvasManualCompare"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <uses-library android:name="android.test.runner"/>
- </application>
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.test.hwuicompare"
- android:label="HW/SW Canvas comparison tool."/>
-
-</manifest>
diff --git a/tests/CanvasCompare/OWNERS b/tests/CanvasCompare/OWNERS
deleted file mode 100644
index c88a9f8..0000000
--- a/tests/CanvasCompare/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /libs/hwui/OWNERS
diff --git a/tests/CanvasCompare/res/drawable/sunset1.jpg b/tests/CanvasCompare/res/drawable/sunset1.jpg
deleted file mode 100644
index 3b4e056..0000000
--- a/tests/CanvasCompare/res/drawable/sunset1.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/CanvasCompare/res/layout/automatic_layout.xml b/tests/CanvasCompare/res/layout/automatic_layout.xml
deleted file mode 100644
index e049ec0..0000000
--- a/tests/CanvasCompare/res/layout/automatic_layout.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <com.android.test.hwuicompare.MainView
- android:id="@+id/hardware_view"
- android:layout_width="@dimen/layer_width"
- android:layout_height="@dimen/layer_width" />
-
- <ImageView
- android:id="@+id/software_image_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true" />
-
- <ImageView
- android:id="@+id/hardware_image_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true" />
-
-</RelativeLayout>
diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml
deleted file mode 100644
index 1a9288c..0000000
--- a/tests/CanvasCompare/res/layout/manual_layout.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical" >
-
- <HorizontalScrollView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
-
- <LinearLayout
- android:id="@+id/spinner_layout"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal" />
- </HorizontalScrollView>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:baselineAligned="true"
- android:orientation="horizontal" >
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="center"
- android:orientation="horizontal" >
-
- <com.android.test.hwuicompare.MainView
- android:id="@+id/hardware_view"
- android:layout_width="@dimen/layer_width"
- android:layout_height="@dimen/layer_width" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="center"
- android:orientation="horizontal" >
-
- <com.android.test.hwuicompare.MainView
- android:id="@+id/software_view"
- android:layout_width="@dimen/layer_width"
- android:layout_height="@dimen/layer_width" />
- </LinearLayout>
- </LinearLayout>
-
- <ImageView
- android:id="@+id/compare_image_view"
- android:layout_width="@dimen/layer_width_double"
- android:layout_height="@dimen/layer_height_double"
- android:filter="false" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="horizontal" >
-
- <ImageButton
- android:id="@+id/previous"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/previous_combination"
- android:src="@android:drawable/ic_media_previous" />
-
- <ImageButton
- android:id="@+id/next"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/next_combination"
- android:src="@android:drawable/ic_media_next" />
-
- <TextView
- android:id="@+id/current_error"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceLarge" />
-
- <Button
- android:id="@+id/show_hardware_version"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/show_hardware_version" />
-
- <Button
- android:id="@+id/show_software_version"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/show_software_version" />
-
- <Button
- android:id="@+id/show_error_heatmap"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/show_error_heatmap" />
- </LinearLayout>
-
-</LinearLayout>
diff --git a/tests/CanvasCompare/res/values/strings.xml b/tests/CanvasCompare/res/values/strings.xml
deleted file mode 100644
index edd4610..0000000
--- a/tests/CanvasCompare/res/values/strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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>
- <string name="app_name">Canvas Compare Test</string>
-
- <!-- show hardware rendered version of the layer -->
- <string name="show_hardware_version">Hardware</string>
- <!-- show software rendered version of the layer -->
- <string name="show_software_version">Software</string>
- <!-- show layer error -->
- <string name="show_error_values">Error</string>
- <!-- show layer error heatmap -->
- <string name="show_error_heatmap">Heatmap</string>
- <!-- select and display the next combination of painting options-->
- <string name="next_combination">Next Combination</string>
- <!-- select and display the previous combination of painting options-->
- <string name="previous_combination">Previous Combination</string>
-</resources>
diff --git a/tests/CanvasCompare/res/values/values.xml b/tests/CanvasCompare/res/values/values.xml
deleted file mode 100644
index f69378d..0000000
--- a/tests/CanvasCompare/res/values/values.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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>
-
- <!-- NOTE: the below MUST be multiples of 64 -->
- <dimen name="layer_height">320px</dimen>
- <dimen name="layer_width">320px</dimen>
-
- <dimen name="layer_height_double">640px</dimen>
- <dimen name="layer_width_double">640px</dimen>
-
-</resources>
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
deleted file mode 100644
index 8ccd4e2..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.test.hwuicompare;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.TreeSet;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Trace;
-import android.util.Log;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-public class AutomaticActivity extends CompareActivity {
- private static final String LOG_TAG = "AutomaticActivity";
- private static final float ERROR_DISPLAY_THRESHOLD = 0.01f;
- protected static final boolean DRAW_BITMAPS = false;
-
- /**
- * Threshold of error change required to consider a test regressed/improved
- */
- private static final float ERROR_CHANGE_THRESHOLD = 0.001f;
-
- private static final float[] ERROR_CUTOFFS = {
- 0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f
- };
-
- private final float[] mErrorRates = new float[ERROR_CUTOFFS.length];
- private float mTotalTests = 0;
- private float mTotalError = 0;
- private int mTestsRegressed = 0;
- private int mTestsImproved = 0;
-
- private ImageView mSoftwareImageView = null;
- private ImageView mHardwareImageView = null;
-
-
- public abstract static class FinalCallback {
- abstract void report(String name, float value);
- void complete() {};
- }
-
- private final ArrayList<FinalCallback> mFinalCallbacks = new ArrayList<FinalCallback>();
-
- Runnable mRunnable = new Runnable() {
- @Override
- public void run() {
- loadBitmaps();
- if (mSoftwareBitmap == null || mHardwareBitmap == null) {
- Log.e(LOG_TAG, "bitmap is null");
- return;
- }
-
- if (DRAW_BITMAPS) {
- mSoftwareImageView.setImageBitmap(mSoftwareBitmap);
- mHardwareImageView.setImageBitmap(mHardwareBitmap);
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "calculateError");
- float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
-
- final String[] modifierNames = DisplayModifier.getLastAppliedModifications();
- handleError(modifierNames, error);
-
- if (DisplayModifier.step()) {
- finishTest();
- } else {
- mHardwareView.invalidate();
- if (DRAW_BITMAPS) {
- mSoftwareImageView.invalidate();
- mHardwareImageView.invalidate();
- }
- }
- mHandler.removeCallbacks(mRunnable);
- }
- };
-
- @Override
- protected void onPause() {
- super.onPause();
- mHandler.removeCallbacks(mRunnable);
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.automatic_layout);
-
- mSoftwareImageView = findViewById(R.id.software_image_view);
- mHardwareImageView = findViewById(R.id.hardware_image_view);
-
- onCreateCommon(mRunnable);
- beginTest();
- }
-
- private static class TestResult {
- TestResult(String label, float error) {
- mLabel = label;
- mTotalError = error;
- mCount = 1;
- }
- public void addInto(float error) {
- mTotalError += error;
- mCount++;
- }
- public float getAverage() {
- return mTotalError / mCount;
- }
- final String mLabel;
- float mTotalError;
- int mCount;
- }
-
- JSONObject mOutputJson = null;
- JSONObject mInputJson = null;
- final HashMap<String, TestResult> mModifierResults = new HashMap<String, TestResult>();
- final HashMap<String, TestResult> mIndividualResults = new HashMap<String, TestResult>();
- final HashMap<String, TestResult> mModifierDiffResults = new HashMap<String, TestResult>();
- final HashMap<String, TestResult> mIndividualDiffResults = new HashMap<String, TestResult>();
- private void beginTest() {
- mFinalCallbacks.add(new FinalCallback() {
- @Override
- void report(String name, float value) {
- Log.d(LOG_TAG, name + " " + value);
- };
- });
-
- File inputFile = new File(Environment.getExternalStorageDirectory(),
- "CanvasCompareInput.json");
- if (inputFile.exists() && inputFile.canRead() && inputFile.length() > 0) {
- try {
- FileInputStream inputStream = new FileInputStream(inputFile);
- Log.d(LOG_TAG, "Parsing input file...");
- StringBuffer content = new StringBuffer((int)inputFile.length());
- byte[] buffer = new byte[1024];
- while (inputStream.read(buffer) != -1) {
- content.append(new String(buffer));
- }
- mInputJson = new JSONObject(content.toString());
- inputStream.close();
- Log.d(LOG_TAG, "Parsed input file with " + mInputJson.length() + " entries");
- } catch (JSONException e) {
- Log.e(LOG_TAG, "error parsing input json", e);
- } catch (IOException e) {
- Log.e(LOG_TAG, "error reading input json from sd", e);
- }
- }
-
- mOutputJson = new JSONObject();
- }
-
- private static void logTestResultHash(String label, HashMap<String, TestResult> map) {
- Log.d(LOG_TAG, "---------------");
- Log.d(LOG_TAG, label + ":");
- Log.d(LOG_TAG, "---------------");
- TreeSet<TestResult> set = new TreeSet<TestResult>(new Comparator<TestResult>() {
- @Override
- public int compare(TestResult lhs, TestResult rhs) {
- if (lhs == rhs) return 0; // don't need to worry about complex equality
-
- int cmp = Float.compare(lhs.getAverage(), rhs.getAverage());
- if (cmp != 0) {
- return cmp;
- }
- return lhs.mLabel.compareTo(rhs.mLabel);
- }
- });
-
- for (TestResult t : map.values()) {
- set.add(t);
- }
-
- for (TestResult t : set.descendingSet()) {
- if (Math.abs(t.getAverage()) > ERROR_DISPLAY_THRESHOLD) {
- Log.d(LOG_TAG, String.format("%2.4f : %s", t.getAverage(), t.mLabel));
- }
- }
- Log.d(LOG_TAG, "");
- }
-
- private void finishTest() {
- for (FinalCallback c : mFinalCallbacks) {
- c.report("averageError", (mTotalError / mTotalTests));
- for (int i = 1; i < ERROR_CUTOFFS.length; i++) {
- c.report(String.format("tests with error over %1.3f", ERROR_CUTOFFS[i]),
- mErrorRates[i]);
- }
- if (mInputJson != null) {
- c.report("tests regressed", mTestsRegressed);
- c.report("tests improved", mTestsImproved);
- }
- c.complete();
- }
-
- try {
- if (mOutputJson != null) {
- String outputString = mOutputJson.toString(4);
- File outputFile = new File(Environment.getExternalStorageDirectory(),
- "CanvasCompareOutput.json");
- FileOutputStream outputStream = new FileOutputStream(outputFile);
- outputStream.write(outputString.getBytes());
- outputStream.close();
- Log.d(LOG_TAG, "Saved output file with " + mOutputJson.length() + " entries");
- }
- } catch (JSONException e) {
- Log.e(LOG_TAG, "error during JSON stringify", e);
- } catch (IOException e) {
- Log.e(LOG_TAG, "error storing JSON output on sd", e);
- }
-
- logTestResultHash("Modifier change vs previous", mModifierDiffResults);
- logTestResultHash("Invidual test change vs previous", mIndividualDiffResults);
- logTestResultHash("Modifier average test results", mModifierResults);
- logTestResultHash("Individual test results", mIndividualResults);
-
- Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show();
- finish();
- }
-
- /**
- * Inserts the error value into all TestResult objects, associated with each of its modifiers
- */
- private static void addForAllModifiers(String fullName, float error, String[] modifierNames,
- HashMap<String, TestResult> modifierResults) {
- for (String modifierName : modifierNames) {
- TestResult r = modifierResults.get(fullName);
- if (r == null) {
- modifierResults.put(modifierName, new TestResult(modifierName, error));
- } else {
- r.addInto(error);
- }
- }
- }
-
- private void handleError(final String[] modifierNames, final float error) {
- String fullName = "";
- for (String s : modifierNames) {
- fullName = fullName.concat("." + s);
- }
- fullName = fullName.substring(1);
-
- float deltaError = 0;
- if (mInputJson != null) {
- try {
- deltaError = error - (float)mInputJson.getDouble(fullName);
- } catch (JSONException e) {
- Log.w(LOG_TAG, "Warning: unable to read from input json", e);
- }
- if (deltaError > ERROR_CHANGE_THRESHOLD) mTestsRegressed++;
- if (deltaError < -ERROR_CHANGE_THRESHOLD) mTestsImproved++;
- mIndividualDiffResults.put(fullName, new TestResult(fullName, deltaError));
- addForAllModifiers(fullName, deltaError, modifierNames, mModifierDiffResults);
- }
-
- mIndividualResults.put(fullName, new TestResult(fullName, error));
- addForAllModifiers(fullName, error, modifierNames, mModifierResults);
-
- try {
- if (mOutputJson != null) {
- mOutputJson.put(fullName, error);
- }
- } catch (JSONException e) {
- Log.e(LOG_TAG, "exception during JSON recording", e);
- mOutputJson = null;
- }
-
- for (int i = 0; i < ERROR_CUTOFFS.length; i++) {
- if (error <= ERROR_CUTOFFS[i]) break;
- mErrorRates[i]++;
- }
- mTotalError += error;
- mTotalTests++;
- }
-
- @Override
- protected boolean forceRecreateBitmaps() {
- // disable, unless needed for drawing into imageviews
- return DRAW_BITMAPS;
- }
-
- // FOR TESTING
- public void setFinalCallback(FinalCallback c) {
- mFinalCallbacks.add(c);
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java
deleted file mode 100644
index 0dec1de..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.test.hwuicompare;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import com.android.test.hwuicompare.R;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Handler;
-import android.os.Trace;
-import android.util.Log;
-import android.view.View;
-
-abstract public class CompareActivity extends Activity {
- private static final String LOG_TAG = "CompareActivity";
-
- protected MainView mHardwareView = null;
-
- protected Bitmap mSoftwareBitmap;
- protected Bitmap mHardwareBitmap;
-
- protected ErrorCalculator mErrorCalculator;
-
- protected Handler mHandler;
-
- Runnable mDrawCallback = null;
- protected boolean mRedrewFlag = true;
-
- protected void onCreateCommon(final Runnable postDrawCallback) {
- mDrawCallback = new Runnable() {
- @Override
- public void run() {
- mRedrewFlag = true;
- mHandler.post(postDrawCallback);
- };
- };
- getWindow().setBackgroundDrawable(new ColorDrawable(0xffefefef));
- ResourceModifiers.init(getResources());
-
- mHardwareView = findViewById(R.id.hardware_view);
- mHardwareView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mHardwareView.setBackgroundColor(Color.WHITE);
- mHardwareView.addDrawCallback(mDrawCallback);
-
- int width = getResources().getDimensionPixelSize(R.dimen.layer_width);
- int height = getResources().getDimensionPixelSize(R.dimen.layer_height);
- mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- mErrorCalculator = new ErrorCalculator(getApplicationContext(), getResources());
-
- mHandler = new Handler();
- }
-
- protected abstract boolean forceRecreateBitmaps();
-
- protected void loadBitmaps() {
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "loadBitmaps");
- if (forceRecreateBitmaps()) {
- int width = mSoftwareBitmap.getWidth();
- int height = mSoftwareBitmap.getHeight();
-
- mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "softwareDraw");
- mHardwareView.draw(new Canvas(mSoftwareBitmap));
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
-
- try {
- Method getHardwareLayer = View.class.getDeclaredMethod("getHardwareLayer");
- if (!getHardwareLayer.isAccessible())
- getHardwareLayer.setAccessible(true);
- Object hardwareLayer = getHardwareLayer.invoke(mHardwareView);
- if (hardwareLayer == null) {
- Log.d(LOG_TAG, "failure to access hardware layer");
- return;
- }
- Method copyInto = hardwareLayer.getClass()
- .getDeclaredMethod("copyInto", Bitmap.class);
- if (!copyInto.isAccessible())
- copyInto.setAccessible(true);
-
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "copyInto");
- boolean success = (Boolean) copyInto.invoke(hardwareLayer, mHardwareBitmap);
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- if (!success) {
- Log.d(LOG_TAG, "failure to copy hardware layer into bitmap");
- }
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
deleted file mode 100644
index 4bcf5a4..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.test.hwuicompare;
-
-import static java.util.Map.entry;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.util.Log;
-
-import java.util.Map;
-import java.util.Map.Entry;
-
-public abstract class DisplayModifier {
-
- // automated tests ignore any combination of operations that don't together return TOTAL_MASK
- protected final static int TOTAL_MASK = 0x1F;
-
- // if we're filling, ensure we're not also sweeping over stroke parameters
- protected final static int SWEEP_STROKE_WIDTH_BIT = 0x1 << 0;
- protected final static int SWEEP_STROKE_CAP_BIT = 0x1 << 1;
- protected final static int SWEEP_STROKE_JOIN_BIT = 0x1 << 2;
-
- protected final static int SWEEP_SHADER_BIT = 0x1 << 3; // only allow non-simple shaders to use rectangle drawing
- protected final static int SWEEP_TRANSFORM_BIT = 0x1 << 4; // only sweep over specified transforms
-
- abstract public void modifyDrawing(Paint paint, Canvas canvas);
- protected int mask() { return 0x0; };
-
- private static final RectF gRect = new RectF(0, 0, 200, 175);
- private static final float[] gPts = new float[] {
- 0, 100, 100, 0, 100, 200, 200, 100
- };
-
- private static final int NUM_PARALLEL_LINES = 24;
- private static final float[] gTriPts = new float[] {
- 75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
- };
- private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
- static {
- int index;
- for (index = 0; index < gTriPts.length; index++) {
- gLinePts[index] = gTriPts[index];
- }
- float val = 0;
- for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = 150;
- gLinePts[index + 1] = val;
- gLinePts[index + 2] = 300;
- gLinePts[index + 3] = val;
- index += 4;
- val += 8 + (2.0f/NUM_PARALLEL_LINES);
- }
- val = 0;
- for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = val;
- gLinePts[index + 1] = 150;
- gLinePts[index + 2] = val;
- gLinePts[index + 3] = 300;
- index += 4;
- val += 8 + (2.0f/NUM_PARALLEL_LINES);
- }
- };
-
- @SuppressWarnings("serial")
- private static final Map<String, Map<String, DisplayModifier>> gMaps = Map.of(
- "aa", Map.of(
- "true", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setAntiAlias(true);
- }
- },
- "false", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setAntiAlias(false);
- }
- }),
- "style", Map.of(
- "fill", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStyle(Paint.Style.FILL);
- }
- },
- "stroke", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStyle(Paint.Style.STROKE);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
- },
- "fillAndStroke", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStyle(Paint.Style.FILL_AND_STROKE);
- }
-
- @Override
- protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
- }),
- "strokeWidth", Map.of(
- "hair", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(0);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
- },
- "0.3", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(0.3f);
- }
- },
- "1", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(1);
- }
- },
- "5", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(5);
- }
- },
- "30", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(30);
- }
- }),
- "strokeCap", Map.of(
- "butt", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeCap(Paint.Cap.BUTT);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_CAP_BIT; }
- },
- "round", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeCap(Paint.Cap.ROUND);
- }
- },
- "square", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeCap(Paint.Cap.SQUARE);
- }
- }),
- "strokeJoin", Map.of(
- "bevel", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeJoin(Paint.Join.BEVEL);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_JOIN_BIT; }
- },
- "round", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeJoin(Paint.Join.ROUND);
- }
- },
- "miter", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeJoin(Paint.Join.MITER);
- }
- }),
- // TODO: add miter0, miter1 etc to test miter distances
- "transform", Map.of(
- "noTransform", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {}
- @Override
- protected int mask() { return SWEEP_TRANSFORM_BIT; };
- },
- "rotate5", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(5);
- }
- },
- "rotate45", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(45);
- }
- },
- "rotate90", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(90);
- canvas.translate(0, -200);
- }
- },
- "scale2x2", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.scale(2, 2);
- }
- @Override
- protected int mask() { return SWEEP_TRANSFORM_BIT; };
- },
- "rot20scl1x4", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(20);
- canvas.scale(1, 4);
- }
- @Override
- protected int mask() { return SWEEP_TRANSFORM_BIT; };
- }),
- "shader", Map.ofEntries(
- entry("noShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {}
- @Override
- protected int mask() { return SWEEP_SHADER_BIT; };
- }),
- entry("repeatShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mRepeatShader);
- }
- @Override
- protected int mask() { return SWEEP_SHADER_BIT; };
- }),
- entry("translatedShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mTranslatedShader);
- }
- }),
- entry("scaledShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mScaledShader);
- }
- }),
- entry("horGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mHorGradient);
- }
- }),
- entry("diagGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mDiagGradient);
- }
- @Override
- protected int mask() { return SWEEP_SHADER_BIT; };
- }),
- entry("vertGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mVertGradient);
- }
- }),
- entry("radGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mRadGradient);
- }
- }),
- entry("sweepGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mSweepGradient);
- }
- }),
- entry("composeShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mComposeShader);
- }
- }),
- entry("bad composeShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mBadComposeShader);
- }
- }),
- entry("bad composeShader 2", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader);
- }
- })),
- "drawing", Map.ofEntries(
- entry("roundRect", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRoundRect(gRect, 20, 20, paint);
- }
- }),
- entry("rect", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRect(gRect, paint);
- }
- @Override
- protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; };
- }),
- entry("circle", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawCircle(100, 100, 75, paint);
- }
- }),
- entry("oval", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawOval(gRect, paint);
- }
- }),
- entry("lines", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawLines(gLinePts, paint);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_CAP_BIT; };
- }),
- entry("plusPoints", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawPoints(gPts, paint);
- }
- }),
- entry("text", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
- canvas.drawText("TEXTTEST", 0, 50, paint);
- }
- }),
- entry("shadowtext", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
- paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
- canvas.drawText("TEXTTEST", 0, 50, paint);
- }
- }),
- entry("bitmapMesh", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawBitmapMesh(ResourceModifiers.instance().mBitmap, 3, 3,
- ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null);
- }
- }),
- entry("arc", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, false, paint);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_CAP_BIT; };
- }),
- entry("arcFromCenter", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, true, paint);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_JOIN_BIT; };
- })));
- // WARNING: DON'T PUT MORE MAPS BELOW THIS
-
- private static Map<String, DisplayModifier> getMapAtIndex(int index) {
- for (Map<String, DisplayModifier> map : gMaps.values()) {
- if (index == 0) {
- return map;
- }
- index--;
- }
- return null;
- }
-
- // indices instead of iterators for easier bidirectional traversal
- private static final int mIndices[] = new int[gMaps.size()];
- private static final String[] mLastAppliedModifications = new String[gMaps.size()];
-
- private static boolean stepInternal(boolean forward) {
- int modifierMapIndex = gMaps.size() - 1;
- while (modifierMapIndex >= 0) {
- Map<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex);
- mIndices[modifierMapIndex] += (forward ? 1 : -1);
-
- if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) {
- break;
- }
-
- mIndices[modifierMapIndex] = (forward ? 0 : map.size() - 1);
- modifierMapIndex--;
- }
- return modifierMapIndex < 0; // true if resetting
- }
-
- public static boolean step() {
- boolean ret = false;
- do {
- ret |= stepInternal(true);
- } while (!checkModificationStateMask());
- return ret;
- }
-
- public static boolean stepBack() {
- boolean ret = false;
- do {
- ret |= stepInternal(false);
- } while (!checkModificationStateMask());
- return ret;
- }
-
- private static boolean checkModificationStateMask() {
- int operatorMask = 0x0;
- int mapIndex = 0;
- for (Map<String, DisplayModifier> map : gMaps.values()) {
- int displayModifierIndex = mIndices[mapIndex];
- for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
- if (displayModifierIndex == 0) {
- mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
- operatorMask |= modifierEntry.getValue().mask();
- break;
- }
- displayModifierIndex--;
- }
- mapIndex++;
- }
- return operatorMask == TOTAL_MASK;
- }
-
- public static void apply(Paint paint, Canvas canvas) {
- int mapIndex = 0;
- for (Map<String, DisplayModifier> map : gMaps.values()) {
- int displayModifierIndex = mIndices[mapIndex];
- for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
- if (displayModifierIndex == 0) {
- mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
- modifierEntry.getValue().modifyDrawing(paint, canvas);
- break;
- }
- displayModifierIndex--;
- }
- mapIndex++;
- }
- }
-
- public static String[] getLastAppliedModifications() {
- return mLastAppliedModifications.clone();
- }
-
- public static String[][] getStrings() {
- String[][] keys = new String[gMaps.size()][];
-
- int i = 0;
- for (Map<String, DisplayModifier> map : gMaps.values()) {
- keys[i] = new String[map.size()];
- int j = 0;
- for (String key : map.keySet()) {
- keys[i][j++] = key;
- }
- i++;
- }
-
- return keys;
- }
-
- public static void setIndex(int mapIndex, int newIndexValue) {
- mIndices[mapIndex] = newIndexValue;
- }
-
- public static int[] getIndices() {
- return mIndices;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java
deleted file mode 100644
index d402699..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.test.hwuicompare;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RenderScript;
-import android.util.Log;
-
-public class ErrorCalculator {
- private static final String LOG_TAG = "ErrorCalculator";
- private static final int REGION_SIZE = 8;
-
- private static final boolean LOG_TIMING = false;
- private static final boolean LOG_CALC = false;
-
- private RenderScript mRS;
- private Allocation mIdealPixelsAllocation;
- private Allocation mGivenPixelsAllocation;
- private Allocation mOutputPixelsAllocation;
-
- private Allocation mInputRowsAllocation;
- private Allocation mOutputRegionsAllocation;
-
- private ScriptC_errorCalculator mScript;
-
- private int[] mOutputRowRegions;
-
- public ErrorCalculator(Context c, Resources resources) {
- int width = resources.getDimensionPixelSize(R.dimen.layer_width);
- int height = resources.getDimensionPixelSize(R.dimen.layer_height);
- mOutputRowRegions = new int[height / REGION_SIZE];
-
- mRS = RenderScript.create(c);
- int[] rowIndices = new int[height / REGION_SIZE];
- for (int i = 0; i < rowIndices.length; i++)
- rowIndices[i] = i * REGION_SIZE;
-
- mScript = new ScriptC_errorCalculator(mRS);
- mScript.set_HEIGHT(height);
- mScript.set_WIDTH(width);
- mScript.set_REGION_SIZE(REGION_SIZE);
-
- mInputRowsAllocation = Allocation.createSized(mRS, Element.I32(mRS), rowIndices.length,
- Allocation.USAGE_SCRIPT);
- mInputRowsAllocation.copyFrom(rowIndices);
- mOutputRegionsAllocation = Allocation.createSized(mRS, Element.I32(mRS),
- mOutputRowRegions.length, Allocation.USAGE_SCRIPT);
- }
-
-
- private static long startMillis, middleMillis;
-
- public float calcErrorRS(Bitmap ideal, Bitmap given) {
- if (LOG_TIMING) {
- startMillis = System.currentTimeMillis();
- }
-
- mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-
- mScript.set_ideal(mIdealPixelsAllocation);
- mScript.set_given(mGivenPixelsAllocation);
-
- mScript.forEach_countInterestingRegions(mInputRowsAllocation, mOutputRegionsAllocation);
- mOutputRegionsAllocation.copyTo(mOutputRowRegions);
-
- int regionCount = 0;
- for (int region : mOutputRowRegions) {
- regionCount += region;
- }
- int interestingPixels = Math.max(1, regionCount) * REGION_SIZE * REGION_SIZE;
-
- if (LOG_TIMING) {
- long startMillis2 = System.currentTimeMillis();
- }
-
- mScript.forEach_accumulateError(mInputRowsAllocation, mOutputRegionsAllocation);
- mOutputRegionsAllocation.copyTo(mOutputRowRegions);
- float totalError = 0;
- for (int row : mOutputRowRegions) {
- totalError += row;
- }
- totalError /= 1024.0f;
-
- if (LOG_TIMING) {
- long finalMillis = System.currentTimeMillis();
- Log.d(LOG_TAG, "rs: first part took " + (middleMillis - startMillis) + "ms");
- Log.d(LOG_TAG, "rs: last part took " + (finalMillis - middleMillis) + "ms");
- }
- if (LOG_CALC) {
- Log.d(LOG_TAG, "rs: error " + totalError + ", pixels " + interestingPixels);
- }
- return totalError / interestingPixels;
- }
-
- public void calcErrorHeatmapRS(Bitmap ideal, Bitmap given, Bitmap output) {
- mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-
- mScript.set_ideal(mIdealPixelsAllocation);
- mScript.set_given(mGivenPixelsAllocation);
-
- mOutputPixelsAllocation = Allocation.createFromBitmap(mRS, output,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mScript.forEach_displayDifference(mOutputPixelsAllocation, mOutputPixelsAllocation);
- mOutputPixelsAllocation.copyTo(output);
- }
-
- public static float calcError(Bitmap ideal, Bitmap given) {
- if (LOG_TIMING) {
- startMillis = System.currentTimeMillis();
- }
-
- int interestingRegions = 0;
- for (int x = 0; x < ideal.getWidth(); x += REGION_SIZE) {
- for (int y = 0; y < ideal.getWidth(); y += REGION_SIZE) {
- if (inspectRegion(ideal, x, y)) {
- interestingRegions++;
- }
- }
- }
-
- int interestingPixels = Math.max(1, interestingRegions) * REGION_SIZE * REGION_SIZE;
-
- if (LOG_TIMING) {
- long startMillis2 = System.currentTimeMillis();
- }
-
- float totalError = 0;
- for (int x = 0; x < ideal.getWidth(); x++) {
- for (int y = 0; y < ideal.getHeight(); y++) {
- int idealColor = ideal.getPixel(x, y);
- int givenColor = given.getPixel(x, y);
- if (idealColor == givenColor)
- continue;
- totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor));
- totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor));
- totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor));
- totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor));
- }
- }
- totalError /= 1024.0f;
- if (LOG_TIMING) {
- long finalMillis = System.currentTimeMillis();
- Log.d(LOG_TAG, "dvk: first part took " + (middleMillis - startMillis) + "ms");
- Log.d(LOG_TAG, "dvk: last part took " + (finalMillis - middleMillis) + "ms");
- }
- if (LOG_CALC) {
- Log.d(LOG_TAG, "dvk: error " + totalError + ", pixels " + interestingPixels);
- }
- return totalError / interestingPixels;
- }
-
- private static boolean inspectRegion(Bitmap ideal, int x, int y) {
- int regionColor = ideal.getPixel(x, y);
- for (int i = 0; i < REGION_SIZE; i++) {
- for (int j = 0; j < REGION_SIZE; j++) {
- if (ideal.getPixel(x + i, y + j) != regionColor)
- return true;
- }
- }
- return false;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java
deleted file mode 100644
index 454fe7b..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.test.hwuicompare;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-public class MainView extends View {
- Paint mPaint = new Paint();
-
- public MainView(Context context) {
- super(context);
- }
-
- public MainView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public MainView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- mPaint.reset();
- DisplayModifier.apply(mPaint, canvas);
-
- if (mDrawCallback != null) {
- mDrawCallback.run();
- }
- }
-
- private Runnable mDrawCallback;
- public void addDrawCallback(Runnable drawCallback) {
- mDrawCallback = drawCallback;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
deleted file mode 100644
index 405ff65..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.test.hwuicompare;
-
-import com.android.test.hwuicompare.R;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-public class ManualActivity extends CompareActivity {
- private static final String LOG_TAG = "ManualActivity";
- private ImageView mCompareImageView;
- private Bitmap mCompareBitmap;
- private TextView mErrorTextView;
- private MainView mSoftwareView;
-
- private static final int COMPARE_VIEW_UNINITIALIZED = -1;
- private static final int COMPARE_VIEW_HARDWARE = 0;
- private static final int COMPARE_VIEW_SOFTWARE = 1;
- private static final int COMPARE_VIEW_HEATMAP = 2; // TODO: add more like this? any ideas?
-
- private int mCompareImageViewState = COMPARE_VIEW_UNINITIALIZED;
- private int mLastCompareImageViewState = COMPARE_VIEW_UNINITIALIZED;
-
- Runnable mRunnable = new Runnable() {
- @Override
- public void run() {
- Log.d(LOG_TAG, "mRunnable running, mRedrewFlag = " + mRedrewFlag);
-
- if (mRedrewFlag) {
- loadBitmaps();
- // recalculate error
- float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
- String modname = "";
- for (String s : DisplayModifier.getLastAppliedModifications()) {
- modname = modname.concat(s + ".");
- }
-
- Log.d(LOG_TAG, "error for " + modname + " is " + error);
- mErrorTextView.setText(String.format("%.4f", error));
- }
-
- if (mCompareImageViewState != mLastCompareImageViewState || mRedrewFlag) {
- switch (mCompareImageViewState) {
- case COMPARE_VIEW_UNINITIALIZED:
- // set to hardware
- case COMPARE_VIEW_HARDWARE:
- mCompareImageView.setImageBitmap(mHardwareBitmap);
- break;
- case COMPARE_VIEW_SOFTWARE:
- mCompareImageView.setImageBitmap(mSoftwareBitmap);
- break;
- case COMPARE_VIEW_HEATMAP:
- mErrorCalculator.calcErrorHeatmapRS(mSoftwareBitmap, mHardwareBitmap,
- mCompareBitmap);
- mCompareImageView.setImageBitmap(mCompareBitmap);
- break;
- }
- mCompareImageView.getDrawable().setFilterBitmap(false);
- mCompareImageView.invalidate();
- }
-
- mLastCompareImageViewState = mCompareImageViewState;
- mRedrewFlag = false;
- mHandler.removeCallbacks(mRunnable);
- }
- };
-
- private void redrawViews() {
- mHardwareView.invalidate();
- mSoftwareView.invalidate();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.manual_layout);
- onCreateCommon(mRunnable);
-
- mSoftwareView = (MainView) findViewById(R.id.software_view);
- mSoftwareView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mSoftwareView.setBackgroundColor(Color.WHITE);
- mSoftwareView.addDrawCallback(mDrawCallback);
-
- mCompareImageView = (ImageView) findViewById(R.id.compare_image_view);
-
- int width = getResources().getDimensionPixelSize(R.dimen.layer_width);
- int height = getResources().getDimensionPixelSize(R.dimen.layer_height);
- mCompareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- mErrorTextView = (TextView) findViewById(R.id.current_error);
- ((ImageButton) findViewById(R.id.next)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- DisplayModifier.step();
- updateSpinners();
- redrawViews();
- }
- });
- ((ImageButton) findViewById(R.id.previous)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- DisplayModifier.stepBack();
- updateSpinners();
- redrawViews();
- }
- });
- ((Button) findViewById(R.id.show_hardware_version))
- .setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCompareImageViewState = COMPARE_VIEW_HARDWARE;
- mHandler.post(mRunnable);
- }
- });
- ((Button) findViewById(R.id.show_software_version))
- .setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCompareImageViewState = COMPARE_VIEW_SOFTWARE;
- mHandler.post(mRunnable);
- }
- });
- ((Button) findViewById(R.id.show_error_heatmap)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCompareImageViewState = COMPARE_VIEW_HEATMAP;
- mHandler.post(mRunnable);
- }
- });
-
- buildSpinnerLayout();
- }
-
- private class DisplayModifierSpinner extends Spinner {
- private final int mIndex;
-
- public DisplayModifierSpinner(int index) {
- super(ManualActivity.this);
- mIndex = index;
- setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parentView, View selectedItem,
- int position, long id) {
- DisplayModifier.setIndex(mIndex, position);
- redrawViews();
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parentView) {
- }
- });
- }
- }
-
- private Spinner[] mSpinners;
-
- private void buildSpinnerLayout() {
- LinearLayout layout = (LinearLayout) findViewById(R.id.spinner_layout);
- String[][] mapsStrings = DisplayModifier.getStrings();
- mSpinners = new Spinner[mapsStrings.length];
- int index = 0;
- for (String[] spinnerValues : mapsStrings) {
- mSpinners[index] = new DisplayModifierSpinner(index);
- mSpinners[index].setAdapter(new ArrayAdapter<String>(this,
- android.R.layout.simple_spinner_dropdown_item, spinnerValues));
- layout.addView(mSpinners[index]);
- index++;
- }
- Log.d(LOG_TAG, "created " + index + " spinners");
- }
-
- private void updateSpinners() {
- int[] indices = DisplayModifier.getIndices();
- for (int i = 0; i < mSpinners.length; i++) {
- mSpinners[i].setSelection(indices[i]);
- }
- }
-
- @Override
- protected boolean forceRecreateBitmaps() {
- // continually recreate bitmaps to avoid modifying bitmaps currently being drawn
- return true;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
deleted file mode 100644
index d522481..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.test.hwuicompare;
-
-import com.android.test.hwuicompare.R;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapShader;
-import android.graphics.Color;
-import android.graphics.ComposeShader;
-import android.graphics.LinearGradient;
-import android.graphics.PorterDuff;
-import android.graphics.RadialGradient;
-import android.graphics.SweepGradient;
-import android.graphics.Matrix;
-import android.graphics.Shader;
-
-public class ResourceModifiers {
- public final BitmapShader mRepeatShader;
- public final BitmapShader mTranslatedShader;
- public final BitmapShader mScaledShader;
- private final int mTexWidth;
- private final int mTexHeight;
- private final float mDrawWidth;
- private final float mDrawHeight;
- public final LinearGradient mHorGradient;
- public final LinearGradient mDiagGradient;
- public final LinearGradient mVertGradient;
- public final RadialGradient mRadGradient;
- public final SweepGradient mSweepGradient;
- public final ComposeShader mComposeShader;
- public final ComposeShader mBadComposeShader;
- public final ComposeShader mAnotherBadComposeShader;
- public final Bitmap mBitmap;
- private final Matrix mMtx1;
- private final Matrix mMtx2;
- private final Matrix mMtx3;
-
- public final float[] mBitmapVertices;
- public final int[] mBitmapColors;
-
- private static ResourceModifiers sInstance = null;
- public static ResourceModifiers instance() { return sInstance; }
- public static void init(Resources resources) {
- sInstance = new ResourceModifiers(resources);
- }
-
- public ResourceModifiers(Resources resources) {
- mBitmap = BitmapFactory.decodeResource(resources, R.drawable.sunset1);
- mTexWidth = mBitmap.getWidth();
- mTexHeight = mBitmap.getHeight();
-
- mDrawWidth = resources.getDimensionPixelSize(R.dimen.layer_width);
- mDrawHeight = resources.getDimensionPixelSize(R.dimen.layer_height);
-
- mRepeatShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT,
- Shader.TileMode.REPEAT);
-
- mTranslatedShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT,
- Shader.TileMode.REPEAT);
- mMtx1 = new Matrix();
- mMtx1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
- mMtx1.postRotate(45, 0, 0);
- mTranslatedShader.setLocalMatrix(mMtx1);
-
- mScaledShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR,
- Shader.TileMode.MIRROR);
- mMtx2 = new Matrix();
- mMtx2.setScale(0.5f, 0.5f);
- mScaledShader.setLocalMatrix(mMtx2);
-
- mHorGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f,
- Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
- mMtx3 = new Matrix();
- mMtx3.setScale(mDrawHeight, 1.0f);
- mMtx3.postRotate(-90.0f);
- mMtx3.postTranslate(0.0f, mDrawHeight);
- mHorGradient.setLocalMatrix(mMtx3);
-
- mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 2.0f, mDrawHeight / 2.0f,
- Color.BLUE, Color.RED, Shader.TileMode.CLAMP);
-
- mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
- Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
-
- mSweepGradient = new SweepGradient(mDrawWidth / 2.0f, mDrawHeight / 2.0f,
- Color.YELLOW, Color.MAGENTA);
-
- mComposeShader = new ComposeShader(mRepeatShader, mHorGradient,
- PorterDuff.Mode.MULTIPLY);
-
- final float width = mBitmap.getWidth() / 8.0f;
- final float height = mBitmap.getHeight() / 8.0f;
-
- mBitmapVertices = new float[] {
- 0.0f, 0.0f, width, 0.0f, width * 2, 0.0f, width * 3, 0.0f,
- 0.0f, height, width, height, width * 2, height, width * 4, height,
- 0.0f, height * 2, width, height * 2, width * 2, height * 2, width * 3, height * 2,
- 0.0f, height * 4, width, height * 4, width * 2, height * 4, width * 4, height * 4,
- };
-
- mBitmapColors = new int[] {
- 0xffff0000, 0xff00ff00, 0xff0000ff, 0xffff0000,
- 0xff0000ff, 0xffff0000, 0xff00ff00, 0xff00ff00,
- 0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00,
- 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000,
- };
-
- // Use a repeating gradient with many colors to test the non simple case.
- mRadGradient = new RadialGradient(mDrawWidth / 4.0f, mDrawHeight / 4.0f, 4.0f,
- mBitmapColors, null, Shader.TileMode.REPEAT);
-
- mBadComposeShader = new ComposeShader(mRadGradient, mComposeShader,
- PorterDuff.Mode.MULTIPLY);
-
- mAnotherBadComposeShader = new ComposeShader(mRadGradient, mVertGradient,
- PorterDuff.Mode.MULTIPLY);
- }
-
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
deleted file mode 100644
index 1ff153c..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.android.test.hwuicompare;
-
-import com.android.test.hwuicompare.AutomaticActivity.FinalCallback;
-
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-
-public class Test extends ActivityInstrumentationTestCase2<AutomaticActivity> {
- AutomaticActivity mActivity;
- private Bundle mBundle;
-
- public Test() {
- super(AutomaticActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mBundle = new Bundle();
- mActivity = getActivity();
- mActivity.setFinalCallback(new FinalCallback() {
-
- @Override
- void report(String key, float value) {
- mBundle.putFloat(key, value);
- }
- @Override
- void complete() {
- synchronized(mBundle) {
- mBundle.notify();
- }
- }
- });
- }
-
- public void testCanvas() {
- synchronized(mBundle) {
- try {
- mBundle.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- getInstrumentation().sendStatus(0, mBundle);
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript
deleted file mode 100644
index 0a1742e..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript
+++ /dev/null
@@ -1,61 +0,0 @@
-#pragma version(1)
-#pragma rs java_package_name(com.android.test.hwuicompare)
-
-int REGION_SIZE;
-int WIDTH;
-int HEIGHT;
-
-rs_allocation ideal;
-rs_allocation given;
-
-void countInterestingRegions(const int32_t *v_in, int32_t *v_out) {
- int y = v_in[0];
- v_out[0] = 0;
-
- for (int x = 0; x < HEIGHT; x += REGION_SIZE) {
- bool interestingRegion = false;
- uchar4 regionColor = rsGetElementAt_uchar4(ideal, x, y);
- for (int i = 0; i < REGION_SIZE && !interestingRegion; i++) {
- for (int j = 0; j < REGION_SIZE && !interestingRegion; j++) {
- uchar4 testVal = rsGetElementAt_uchar4(ideal, x + j, y + i);
- interestingRegion |= (testVal.r != regionColor.r);
- interestingRegion |= (testVal.g != regionColor.g);
- interestingRegion |= (testVal.b != regionColor.b);
- interestingRegion |= (testVal.a != regionColor.a);
- }
- }
- if (interestingRegion) {
- v_out[0]++;
- }
- }
-}
-
-void accumulateError(const int32_t *v_in, int32_t *v_out) {
- int startY = v_in[0];
- int error = 0;
- for (int y = startY; y < startY + REGION_SIZE; y++) {
- for (int x = 0; x < HEIGHT; x++) {
- uchar4 idealPixel = rsGetElementAt_uchar4(ideal, x, y);
- uchar4 givenPixel = rsGetElementAt_uchar4(given, x, y);
-
- error += abs(idealPixel.x - givenPixel.x);
- error += abs(idealPixel.y - givenPixel.y);
- error += abs(idealPixel.z - givenPixel.z);
- error += abs(idealPixel.w - givenPixel.w);
- }
- }
- v_out[0] = error;
-}
-
-void displayDifference(const uchar4 *v_in, uchar4 *v_out, uint32_t x, uint32_t y) {
- float4 idealPixel = rsGetElementAt_float4(ideal, x, y);
- float4 givenPixel = rsGetElementAt_float4(given, x, y);
-
- float4 diff = idealPixel - givenPixel;
- float totalDiff = diff.x + diff.y + diff.z + diff.w;
- if (totalDiff < 0) {
- v_out[0] = rsPackColorTo8888(0, 0, clamp(-totalDiff/2.f, 0.f, 1.f));
- } else {
- v_out[0] = rsPackColorTo8888(clamp(totalDiff/2.f, 0.f, 1.f), 0, 0);
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
index 52ca7a2..75de476 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -114,6 +114,10 @@
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+ @FlakyTest(bugId = 266730606)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 41316d8..8d2af38 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.RequiresDevice
import android.view.KeyEvent
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerBuilder
import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.FlickerTestFactory
@@ -140,6 +140,12 @@
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsVisibleAtStartAndEnd() {
+ super.navBarWindowIsVisibleAtStartAndEnd()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index d1957fb..c66f4e5 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -16,6 +16,8 @@
#include "DumpManifest.h"
+#include <androidfw/ApkParsing.h>
+
#include <algorithm>
#include <array>
#include <memory>
@@ -2729,19 +2731,25 @@
}));
supports_screen_ = screen ? screen : &default_screens;
- // Gather the supported architectures_ of the app
- std::set<std::string> architectures_from_apk;
+ bool has_renderscript_bitcode = false;
auto it = apk_->GetFileCollection()->Iterator();
while (it->HasNext()) {
- auto file_path = it->Next()->GetSource().path;
- if (file_path.starts_with("lib/")) {
- file_path = file_path.substr(4);
- size_t pos = file_path.find('/');
- if (pos != std::string::npos) {
- file_path = file_path.substr(0, pos);
- }
+ if (it->Next()->GetSource().path.ends_with(".bc")) {
+ has_renderscript_bitcode = true;
+ break;
+ }
+ }
- architectures_from_apk.insert(file_path);
+ // Gather the supported architectures_ of the app
+ std::set<std::string> architectures_from_apk;
+ it = apk_->GetFileCollection()->Iterator();
+ while (it->HasNext()) {
+ auto file_path = it->Next()->GetSource().path.c_str();
+
+ const char* last_slash =
+ android::util::ValidLibraryPathLastSlash(file_path, has_renderscript_bitcode, false);
+ if (last_slash) {
+ architectures_from_apk.insert(std::string(file_path + APK_LIB_LEN, last_slash));
}
}
diff --git a/tools/lint/global/Android.bp b/tools/lint/global/Android.bp
index 3756abe..bedb7bd 100644
--- a/tools/lint/global/Android.bp
+++ b/tools/lint/global/Android.bp
@@ -38,12 +38,6 @@
java_test_host {
name: "AndroidGlobalLintCheckerTest",
- // TODO(b/239881504): Since this test was written, Android
- // Lint was updated, and now includes classes that were
- // compiled for java 15. The soong build doesn't support
- // java 15 yet, so we can't compile against "lint". Disable
- // the test until java 15 is supported.
- enabled: false,
srcs: ["checks/src/test/java/**/*.kt"],
static_libs: [
"AndroidGlobalLintChecker",
@@ -53,5 +47,19 @@
],
test_options: {
unit_test: true,
+ tradefed_options: [
+ {
+ // lint bundles in some classes that were built with older versions
+ // of libraries, and no longer load. Since tradefed tries to load
+ // all classes in the jar to look for tests, it crashes loading them.
+ // Exclude these classes from tradefed's search.
+ name: "exclude-paths",
+ value: "org/apache",
+ },
+ {
+ name: "exclude-paths",
+ value: "META-INF",
+ },
+ ],
},
}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
index 72de00f..dcfbe95 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
@@ -29,6 +29,8 @@
const val BINDER_CLASS = "android.os.Binder"
const val IINTERFACE_INTERFACE = "android.os.IInterface"
+const val AIDL_PERMISSION_HELPER_SUFFIX = "_enforcePermission"
+
/**
* If a non java (e.g. c++) backend is enabled, the @EnforcePermission
* annotation cannot be used. At time of writing, the mechanism
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
index 485765b..25d208d 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -36,6 +36,7 @@
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UExpressionList
import org.jetbrains.uast.UIfExpression
+import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UThrowExpression
import org.jetbrains.uast.UastBinaryOperator
import org.jetbrains.uast.evaluateString
@@ -46,29 +47,37 @@
* Helper class that facilitates the creation of lint auto fixes
*/
data class EnforcePermissionFix(
- val locations: List<Location>,
+ val manualCheckLocations: List<Location>,
val permissionNames: List<String>,
val errorLevel: Boolean,
val anyOf: Boolean,
) {
- fun toLintFix(annotationLocation: Location): LintFix {
- val removeFixes = this.locations.map {
- LintFix.create()
- .replace()
- .reformat(true)
- .range(it)
- .with("")
- .autoFix()
- .build()
+ fun toLintFix(context: JavaContext, node: UMethod): LintFix {
+ val methodLocation = context.getLocation(node)
+ val replaceOrRemoveFixes = manualCheckLocations.mapIndexed { index, manualCheckLocation ->
+ if (index == 0) {
+ // Replace the first manual check with a call to the helper method
+ getHelperMethodFix(node, manualCheckLocation, false)
+ } else {
+ // Remove all subsequent manual checks
+ LintFix.create()
+ .replace()
+ .reformat(true)
+ .range(manualCheckLocation)
+ .with("")
+ .autoFix()
+ .build()
+ }
}
+ // Annotate the method with @EnforcePermission(...)
val annotateFix = LintFix.create()
- .annotate(this.annotation)
- .range(annotationLocation)
+ .annotate(annotation)
+ .range(methodLocation)
.autoFix()
.build()
- return LintFix.create().composite(annotateFix, *removeFixes.toTypedArray())
+ return LintFix.create().composite(annotateFix, *replaceOrRemoveFixes.toTypedArray())
}
private val annotation: String
@@ -89,7 +98,50 @@
companion object {
/**
+ * Walks the expressions in a block, looking for simple permission checks.
+ *
+ * As soon as something other than a permission check is encountered, stop looking,
+ * as some other business logic is happening that prevents an automated fix.
+ */
+ fun fromBlockExpression(
+ context: JavaContext,
+ blockExpression: UBlockExpression
+ ): EnforcePermissionFix? {
+ try {
+ val singleFixes = mutableListOf<EnforcePermissionFix>()
+ for (expression in blockExpression.expressions) {
+ val fix = fromExpression(context, expression) ?: break
+ singleFixes.add(fix)
+ }
+ return compose(singleFixes)
+ } catch (e: AnyOfAllOfException) {
+ return null
+ }
+ }
+
+ /**
+ * Conditionally constructs EnforcePermissionFix from any UExpression
+ *
+ * @return EnforcePermissionFix if the expression boils down to a permission check,
+ * else null
+ */
+ fun fromExpression(
+ context: JavaContext,
+ expression: UExpression
+ ): EnforcePermissionFix? {
+ val trimmedExpression = expression.skipParenthesizedExprDown()
+ if (trimmedExpression is UIfExpression) {
+ return fromIfExpression(context, trimmedExpression)
+ }
+ findCallExpression(trimmedExpression)?.let {
+ return fromCallExpression(context, it)
+ }
+ return null
+ }
+
+ /**
* Conditionally constructs EnforcePermissionFix from a UCallExpression
+ *
* @return EnforcePermissionFix if the called method is annotated with @PermissionMethod, else null
*/
fun fromCallExpression(
@@ -111,6 +163,7 @@
/**
* Conditionally constructs EnforcePermissionFix from a UCallExpression
+ *
* @return EnforcePermissionFix IF AND ONLY IF:
* * The condition of the if statement compares the return value of a
* PermissionMethod to one of the PackageManager.PermissionResult values
@@ -172,7 +225,8 @@
}
- fun compose(individuals: List<EnforcePermissionFix>): EnforcePermissionFix {
+ fun compose(individuals: List<EnforcePermissionFix>): EnforcePermissionFix? {
+ if (individuals.isEmpty()) return null
val anyOfs = individuals.filter(EnforcePermissionFix::anyOf)
// anyOf/allOf should be consistent. If we encounter some @PermissionMethods that are anyOf
// and others that aren't, we don't know what to do.
@@ -180,7 +234,7 @@
throw AnyOfAllOfException()
}
return EnforcePermissionFix(
- individuals.flatMap(EnforcePermissionFix::locations),
+ individuals.flatMap(EnforcePermissionFix::manualCheckLocations),
individuals.flatMap(EnforcePermissionFix::permissionNames),
errorLevel = individuals.all(EnforcePermissionFix::errorLevel),
anyOf = anyOfs.isNotEmpty()
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
index b65c0fc..df13af5 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
@@ -55,7 +55,7 @@
return
}
- val targetExpression = "${node.name}$HELPER_SUFFIX()"
+ val targetExpression = getHelperMethodCallSourceString(node)
val message =
"Method must start with $targetExpression or super.${node.name}(), if applicable"
@@ -85,22 +85,11 @@
val locationTarget = getLocationTarget(firstExpression)
val expressionLocation = context.getLocation(locationTarget)
- val indent = " ".repeat(expressionLocation.start?.column ?: 0)
-
- val fix = fix()
- .replace()
- .range(expressionLocation)
- .beginning()
- .with("$targetExpression;\n\n$indent")
- .reformat(true)
- .autoFix()
- .build()
-
context.report(
ISSUE_ENFORCE_PERMISSION_HELPER,
context.getLocation(node),
message,
- fix
+ getHelperMethodFix(node, expressionLocation),
)
}
}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
index 2239ea1..d41fee3 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
@@ -17,6 +17,8 @@
package com.google.android.lint.aidl
import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Location
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiReferenceList
import org.jetbrains.uast.UMethod
@@ -69,3 +71,26 @@
references != null &&
references.referenceElements.size == 1 &&
references.referenceElements[0].qualifiedName == qualifiedName
+
+fun getHelperMethodCallSourceString(node: UMethod) = "${node.name}$AIDL_PERMISSION_HELPER_SUFFIX()"
+
+fun getHelperMethodFix(
+ node: UMethod,
+ manualCheckLocation: Location,
+ prepend: Boolean = true
+): LintFix {
+ val helperMethodSource = getHelperMethodCallSourceString(node)
+ val indent = " ".repeat(manualCheckLocation.start?.column ?: 0)
+ val newText = "$helperMethodSource;${if (prepend) "\n\n$indent" else ""}"
+
+ val fix = LintFix.create()
+ .replace()
+ .range(manualCheckLocation)
+ .with(newText)
+ .reformat(true)
+ .autoFix()
+
+ if (prepend) fix.beginning()
+
+ return fix.build()
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
index 11a283a..c7be36e 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
@@ -23,19 +23,13 @@
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
-import com.google.android.lint.findCallExpression
import org.jetbrains.uast.UBlockExpression
-import org.jetbrains.uast.UElement
-import org.jetbrains.uast.UIfExpression
import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.skipParenthesizedExprDown
/**
* Looks for methods implementing generated AIDL interface stubs
* that can have simple permission checks migrated to
* @EnforcePermission annotations
- *
- * TODO: b/242564870 (enable parse and autoFix of .aidl files)
*/
@Suppress("UnstableApiUsage")
class SimpleManualPermissionEnforcementDetector : AidlImplementationDetector() {
@@ -45,8 +39,8 @@
interfaceName: String,
body: UBlockExpression
) {
- val enforcePermissionFix = accumulateSimplePermissionCheckFixes(body, context) ?: return
- val lintFix = enforcePermissionFix.toLintFix(context.getLocation(node))
+ val enforcePermissionFix = EnforcePermissionFix.fromBlockExpression(context, body) ?: return
+ val lintFix = enforcePermissionFix.toLintFix(context, node)
val message =
"$interfaceName permission check ${
if (enforcePermissionFix.errorLevel) "should" else "can"
@@ -54,68 +48,19 @@
val incident = Incident(
ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
- enforcePermissionFix.locations.last(),
+ enforcePermissionFix.manualCheckLocations.last(),
message,
lintFix
)
- if (enforcePermissionFix.errorLevel) {
- incident.overrideSeverity(Severity.ERROR)
- }
+ // TODO(b/265014041): turn on errors once all code that would cause one is fixed
+ // if (enforcePermissionFix.errorLevel) {
+ // incident.overrideSeverity(Severity.ERROR)
+ // }
context.report(incident)
}
- /**
- * Walk the expressions in the method, looking for simple permission checks.
- *
- * If a single permission check is found at the beginning of the method,
- * this should be migrated to @EnforcePermission(value).
- *
- * If multiple consecutive permission checks are found,
- * these should be migrated to @EnforcePermission(allOf={value1, value2, ...})
- *
- * As soon as something other than a permission check is encountered, stop looking,
- * as some other business logic is happening that prevents an automated fix.
- */
- private fun accumulateSimplePermissionCheckFixes(
- methodBody: UBlockExpression,
- context: JavaContext
- ): EnforcePermissionFix? {
- try {
- val singleFixes = mutableListOf<EnforcePermissionFix>()
- for (expression in methodBody.expressions) {
- val fix = getPermissionCheckFix(
- expression.skipParenthesizedExprDown(),
- context) ?: break
- singleFixes.add(fix)
- }
- return when (singleFixes.size) {
- 0 -> null
- 1 -> singleFixes[0]
- else -> EnforcePermissionFix.compose(singleFixes)
- }
- } catch (e: AnyOfAllOfException) {
- return null
- }
- }
-
-
- /**
- * If an expression boils down to a permission check, return
- * the helper for creating a lint auto fix to @EnforcePermission
- */
- private fun getPermissionCheckFix(startingExpression: UElement?, context: JavaContext):
- EnforcePermissionFix? {
- if (startingExpression is UIfExpression) {
- return EnforcePermissionFix.fromIfExpression(context, startingExpression)
- }
- findCallExpression(startingExpression)?.let {
- return EnforcePermissionFix.fromCallExpression(context, it)
- }
- return null
- }
-
companion object {
private val EXPLANATION = """
@@ -142,7 +87,6 @@
SimpleManualPermissionEnforcementDetector::class.java,
Scope.JAVA_FILE_SCOPE
),
- enabledByDefault = false, // TODO: enable once b/241171714 is resolved
)
}
}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
index 2ac550b..6b8e72cf 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
@@ -51,10 +51,10 @@
.run()
.expect(
"""
- src/Foo.java:7: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:7: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
@@ -64,6 +64,7 @@
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -7 +8
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ + test_enforcePermission();
"""
)
}
@@ -101,6 +102,7 @@
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -7 +8
- mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+ + test_enforcePermission();
"""
)
}
@@ -138,6 +140,7 @@
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -7 +8
- mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ + test_enforcePermission();
"""
)
}
@@ -165,10 +168,10 @@
.run()
.expect(
"""
- src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:8: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(
^
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
@@ -179,6 +182,7 @@
@@ -8 +9
- mContext.enforceCallingOrSelfPermission(
- "android.permission.READ_CONTACTS", "foo");
+ + test_enforcePermission();
"""
)
}
@@ -205,19 +209,20 @@
.run()
.expect(
"""
- src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:8: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
"""
- Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+ Fix for src/Foo.java line 8: Annotate with @EnforcePermission:
@@ -6 +6
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -8 +9
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
+ + test_enforcePermission();
"""
)
}
@@ -247,10 +252,10 @@
.run()
.expect(
"""
- src/Foo.java:10: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:10: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(
^
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
@@ -263,98 +268,101 @@
- "android.permission.READ_CONTACTS", "foo");
- mContext.enforceCallingOrSelfPermission(
- "android.permission.WRITE_CONTACTS", "foo");
+ + test_enforcePermission();
"""
)
}
fun testAllOf_mixedOrSelf_warning() {
lint().files(
- java(
- """
- import android.content.Context;
- import android.test.ITest;
- public class Foo {
- private Context mContext;
- private ITest itest = new ITest.Stub() {
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(
- "android.permission.READ_CONTACTS", "foo");
- mContext.enforceCallingPermission(
- "android.permission.WRITE_CONTACTS", "foo");
- }
- };
- }
- """
- ).indented(),
- *stubs
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.enforceCallingPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
)
- .run()
- .expect(
- """
- src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
- mContext.enforceCallingPermission(
- ^
- 0 errors, 1 warnings
- """
- )
- .expectFixDiffs(
- """
- Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
- @@ -6 +6
- + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
- @@ -8 +9
- - mContext.enforceCallingOrSelfPermission(
- - "android.permission.READ_CONTACTS", "foo");
- - mContext.enforceCallingPermission(
- - "android.permission.WRITE_CONTACTS", "foo");
- """
- )
+ .run()
+ .expect(
+ """
+ src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.enforceCallingPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.permission.READ_CONTACTS", "foo");
+ - mContext.enforceCallingPermission(
+ - "android.permission.WRITE_CONTACTS", "foo");
+ + test_enforcePermission();
+ """
+ )
}
fun testAllOf_mixedEnforces_warning() {
lint().files(
- java(
- """
- import android.content.Context;
- import android.test.ITest;
- public class Foo {
- private Context mContext;
- private ITest itest = new ITest.Stub() {
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(
- "android.permission.READ_CONTACTS", "foo");
- mContext.checkCallingOrSelfPermission(
- "android.permission.WRITE_CONTACTS", "foo");
- }
- };
- }
- """
- ).indented(),
- *stubs
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.checkCallingOrSelfPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
)
- .run()
- .expect(
- """
- src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
- mContext.checkCallingOrSelfPermission(
- ^
- 0 errors, 1 warnings
- """
- )
- .expectFixDiffs(
- """
- Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
- @@ -6 +6
- + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
- @@ -8 +9
- - mContext.enforceCallingOrSelfPermission(
- - "android.permission.READ_CONTACTS", "foo");
- - mContext.checkCallingOrSelfPermission(
- - "android.permission.WRITE_CONTACTS", "foo");
- """
- )
+ .run()
+ .expect(
+ """
+ src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.checkCallingOrSelfPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.permission.READ_CONTACTS", "foo");
+ - mContext.checkCallingOrSelfPermission(
+ - "android.permission.WRITE_CONTACTS", "foo");
+ + test_enforcePermission();
+ """
+ )
}
fun testPrecedingExpressions() {
@@ -389,7 +397,7 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod(orSelf = true)
+ @android.annotation.PermissionMethod(orSelf = true)
private void helper() {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
@@ -406,10 +414,10 @@
.run()
.expect(
"""
- src/Foo.java:14: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:14: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
helper();
~~~~~~~~~
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
@@ -419,6 +427,7 @@
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -14 +15
- helper();
+ + test_enforcePermission();
"""
)
}
@@ -433,7 +442,7 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod
+ @android.annotation.PermissionMethod
private void helper() {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
@@ -463,6 +472,7 @@
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -14 +15
- helper();
+ + test_enforcePermission();
"""
)
}
@@ -477,7 +487,7 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod(orSelf = true)
+ @android.annotation.PermissionMethod(orSelf = true)
private void helper() {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
mContext.enforceCallingOrSelfPermission("android.permission.WRITE_CONTACTS", "foo");
@@ -496,10 +506,10 @@
.run()
.expect(
"""
- src/Foo.java:16: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:16: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission("FOO", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
@@ -510,6 +520,7 @@
@@ -15 +16
- helper();
- mContext.enforceCallingOrSelfPermission("FOO", "foo");
+ + test_enforcePermission();
"""
)
}
@@ -525,13 +536,13 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod(orSelf = true)
+ @android.annotation.PermissionMethod(orSelf = true)
private void helperHelper() {
helper("android.permission.WRITE_CONTACTS");
}
- @android.content.pm.PermissionMethod(orSelf = true)
- private void helper(@android.content.pm.PermissionName String extraPermission) {
+ @android.annotation.PermissionMethod(orSelf = true)
+ private void helper(@android.annotation.PermissionName String extraPermission) {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
@@ -547,10 +558,10 @@
.run()
.expect(
"""
- src/Foo.java:19: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:19: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
helperHelper();
~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
@@ -560,6 +571,7 @@
+ @android.annotation.EnforcePermission(allOf={"android.permission.WRITE_CONTACTS", "android.permission.READ_CONTACTS"})
@@ -19 +20
- helperHelper();
+ + test_enforcePermission();
"""
)
}
@@ -587,10 +599,10 @@
.run()
.expect(
"""
- src/Foo.java:7: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:7: Warning: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
if (mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo")
^
- 1 errors, 0 warnings
+ 0 errors, 1 warnings
"""
)
.expectFixDiffs(
@@ -603,76 +615,106 @@
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("yikes!");
- }
+ + test_enforcePermission();
"""
)
}
fun testIfExpression_orSelfFalse_warning() {
lint().files(
- java(
- """
- import android.content.Context;
- import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
- @Override
- public void test() throws android.os.RemoteException {
- if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("yikes!");
- }
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("yikes!");
}
}
- """
- ).indented(),
- *stubs
+ }
+ """
+ ).indented(),
+ *stubs
)
- .run()
- .expect(
- """
- src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
- if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
- ^
- 0 errors, 1 warnings
- """
- )
- .expectFixDiffs(
- """
- Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
- @@ -5 +5
- + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
- @@ -7 +8
- - if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
- - != PackageManager.PERMISSION_GRANTED) {
- - throw new SecurityException("yikes!");
- - }
- """
- )
+ .run()
+ .expect(
+ """
+ src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+ @@ -5 +5
+ + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+ @@ -7 +8
+ - if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
+ - != PackageManager.PERMISSION_GRANTED) {
+ - throw new SecurityException("yikes!");
+ - }
+ + test_enforcePermission();
+ """
+ )
}
fun testIfExpression_otherSideEffect_ignored() {
lint().files(
- java(
- """
- import android.content.Context;
- import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
- @Override
- public void test() throws android.os.RemoteException {
- if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
- != PackageManager.PERMISSION_GRANTED) {
- doSomethingElse();
- throw new SecurityException("yikes!");
- }
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ if (mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
+ != PackageManager.PERMISSION_GRANTED) {
+ doSomethingElse();
+ throw new SecurityException("yikes!");
}
}
- """
- ).indented(),
- *stubs
+ }
+ """
+ ).indented(),
+ *stubs
)
- .run()
- .expectClean()
+ .run()
+ .expectClean()
+ }
+
+ fun testIfExpression_inlinedWithSideEffect_ignored() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ if (somethingElse() && mContext.checkCallingPermission("android.permission.READ_CONTACTS", "foo")
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("yikes!");
+ }
+ }
+
+ private boolean somethingElse() {
+ return true;
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
}
fun testAnyOf_hardCodedAndVarArgs() {
@@ -685,13 +727,13 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod(anyOf = true)
+ @android.annotation.PermissionMethod(anyOf = true)
private void helperHelper() {
helper("FOO", "BAR");
}
- @android.content.pm.PermissionMethod(anyOf = true, value = {"BAZ", "BUZZ"})
- private void helper(@android.content.pm.PermissionName String... extraPermissions) {}
+ @android.annotation.PermissionMethod(anyOf = true, value = {"BAZ", "BUZZ"})
+ private void helper(@android.annotation.PermissionName String... extraPermissions) {}
@Override
public void test() throws android.os.RemoteException {
@@ -718,6 +760,7 @@
+ @android.annotation.EnforcePermission(anyOf={"BAZ", "BUZZ", "FOO", "BAR"})
@@ -17 +18
- helperHelper();
+ + test_enforcePermission();
"""
)
}
@@ -733,13 +776,13 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod
+ @android.annotation.PermissionMethod
private void allOfhelper() {
mContext.enforceCallingOrSelfPermission("FOO");
mContext.enforceCallingOrSelfPermission("BAR");
}
- @android.content.pm.PermissionMethod(anyOf = true, permissions = {"BAZ", "BUZZ"})
+ @android.annotation.PermissionMethod(anyOf = true, permissions = {"BAZ", "BUZZ"})
private void anyOfHelper() {}
@Override
@@ -761,17 +804,18 @@
java(
"""
import android.content.Context;
- import android.content.pm.PermissionName;import android.test.ITest;
+ import android.annotation.PermissionName;
+ import android.test.ITest;
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod(anyOf = true)
+ @android.annotation.PermissionMethod(anyOf = true)
private void anyOfCheck(@PermissionName String... permissions) {
allOfCheck("BAZ", "BUZZ");
}
- @android.content.pm.PermissionMethod
+ @android.annotation.PermissionMethod
private void allOfCheck(@PermissionName String... permissions) {}
@Override
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
index f6e58da..2ec8fdd 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
@@ -5,84 +5,84 @@
val aidlStub: TestFile = java(
"""
- package android.test;
- public interface ITest extends android.os.IInterface {
- public static abstract class Stub extends android.os.Binder implements android.test.ITest {
- protected void test_enforcePermission() throws SecurityException {}
- }
- public void test() throws android.os.RemoteException;
+ package android.test;
+ public interface ITest extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements android.test.ITest {
+ protected void test_enforcePermission() throws SecurityException {}
}
+ public void test() throws android.os.RemoteException;
+ }
"""
).indented()
val contextStub: TestFile = java(
"""
- package android.content;
- public class Context {
- @android.content.pm.PermissionMethod(orSelf = true)
- public void enforceCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
- @android.content.pm.PermissionMethod
- public void enforceCallingPermission(@android.content.pm.PermissionName String permission, String message) {}
- @android.content.pm.PermissionMethod(orSelf = true)
- public int checkCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
- @android.content.pm.PermissionMethod
- public int checkCallingPermission(@android.content.pm.PermissionName String permission, String message) {}
- }
+ package android.content;
+ public class Context {
+ @android.annotation.PermissionMethod(orSelf = true)
+ public void enforceCallingOrSelfPermission(@android.annotation.PermissionName String permission, String message) {}
+ @android.annotation.PermissionMethod
+ public void enforceCallingPermission(@android.annotation.PermissionName String permission, String message) {}
+ @android.annotation.PermissionMethod(orSelf = true)
+ public int checkCallingOrSelfPermission(@android.annotation.PermissionName String permission, String message) {}
+ @android.annotation.PermissionMethod
+ public int checkCallingPermission(@android.annotation.PermissionName String permission, String message) {}
+ }
"""
).indented()
val binderStub: TestFile = java(
"""
- package android.os;
- public class Binder {
- public static int getCallingUid() {}
- }
+ package android.os;
+ public class Binder {
+ public static int getCallingUid() {}
+ }
"""
).indented()
val permissionMethodStub: TestFile = java(
-"""
- package android.content.pm;
+ """
+ package android.annotation;
- import static java.lang.annotation.ElementType.METHOD;
- import static java.lang.annotation.RetentionPolicy.CLASS;
+ import static java.lang.annotation.ElementType.METHOD;
+ import static java.lang.annotation.RetentionPolicy.CLASS;
- import java.lang.annotation.Retention;
- import java.lang.annotation.Target;
+ import java.lang.annotation.Retention;
+ import java.lang.annotation.Target;
- @Retention(CLASS)
- @Target({METHOD})
- public @interface PermissionMethod {}
+ @Retention(CLASS)
+ @Target({METHOD})
+ public @interface PermissionMethod {}
"""
).indented()
val permissionNameStub: TestFile = java(
-"""
- package android.content.pm;
+ """
+ package android.annotation;
- import static java.lang.annotation.ElementType.FIELD;
- import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
- import static java.lang.annotation.ElementType.METHOD;
- import static java.lang.annotation.ElementType.PARAMETER;
- import static java.lang.annotation.RetentionPolicy.CLASS;
+ import static java.lang.annotation.ElementType.FIELD;
+ import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+ import static java.lang.annotation.ElementType.METHOD;
+ import static java.lang.annotation.ElementType.PARAMETER;
+ import static java.lang.annotation.RetentionPolicy.CLASS;
- import java.lang.annotation.Retention;
- import java.lang.annotation.Target;
+ import java.lang.annotation.Retention;
+ import java.lang.annotation.Target;
- @Retention(CLASS)
- @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
- public @interface PermissionName {}
+ @Retention(CLASS)
+ @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
+ public @interface PermissionName {}
"""
).indented()
val manifestStub: TestFile = java(
"""
- package android;
+ package android;
- public final class Manifest {
- public static final class permission {
- public static final String READ_CONTACTS="android.permission.READ_CONTACTS";
- }
+ public final class Manifest {
+ public static final class permission {
+ public static final String READ_CONTACTS="android.permission.READ_CONTACTS";
}
+ }
""".trimIndent()
)
\ No newline at end of file
diff --git a/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java
index 1d479fc..4a821bb 100644
--- a/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java
+++ b/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java
@@ -21,6 +21,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -35,6 +36,7 @@
public boolean enable6GhzRnr;
public ArrayList<ChannelSettings> channelSettings;
public ArrayList<HiddenNetwork> hiddenNetworks;
+ public byte[] vendorIes;
/** public constructor */
public SingleScanSettings() { }
@@ -53,13 +55,15 @@
return scanType == settings.scanType
&& enable6GhzRnr == settings.enable6GhzRnr
&& channelSettings.equals(settings.channelSettings)
- && hiddenNetworks.equals(settings.hiddenNetworks);
+ && hiddenNetworks.equals(settings.hiddenNetworks)
+ && Arrays.equals(vendorIes, settings.vendorIes);
}
/** override hash code */
@Override
public int hashCode() {
- return Objects.hash(scanType, channelSettings, hiddenNetworks, enable6GhzRnr);
+ return Objects.hash(scanType, channelSettings, hiddenNetworks, enable6GhzRnr,
+ Arrays.hashCode(vendorIes));
}
@@ -88,6 +92,11 @@
out.writeBoolean(enable6GhzRnr);
out.writeTypedList(channelSettings);
out.writeTypedList(hiddenNetworks);
+ if (vendorIes == null) {
+ out.writeByteArray(new byte[0]);
+ } else {
+ out.writeByteArray(vendorIes);
+ }
}
/** implement Parcelable interface */
@@ -108,6 +117,10 @@
in.readTypedList(result.channelSettings, ChannelSettings.CREATOR);
result.hiddenNetworks = new ArrayList<HiddenNetwork>();
in.readTypedList(result.hiddenNetworks, HiddenNetwork.CREATOR);
+ result.vendorIes = in.createByteArray();
+ if (result.vendorIes == null) {
+ result.vendorIes = new byte[0];
+ }
if (in.dataAvail() != 0) {
Log.e(TAG, "Found trailing data after parcel parsing.");
}
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 2ad5771..2a199d2 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -96,6 +96,10 @@
public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR =
"android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR";
+ // Extra scanning parameter used to add vendor IEs (byte[]).
+ public static final String EXTRA_SCANNING_PARAM_VENDOR_IES =
+ "android.net.wifi.nl80211.extra.SCANNING_PARAM_VENDOR_IES";
+
private AlarmManager mAlarmManager;
private Handler mEventHandler;
@@ -1135,6 +1139,7 @@
settings.hiddenNetworks = new ArrayList<>();
if (extraScanningParams != null) {
settings.enable6GhzRnr = extraScanningParams.getBoolean(SCANNING_PARAM_ENABLE_6GHZ_RNR);
+ settings.vendorIes = extraScanningParams.getByteArray(EXTRA_SCANNING_PARAM_VENDOR_IES);
}
if (freqs != null) {
@@ -1169,7 +1174,7 @@
case IWifiScannerImpl.SCAN_STATUS_FAILED_ABORT:
return WifiScanner.REASON_ABORT;
case IWifiScannerImpl.SCAN_STATUS_FAILED_NODEV:
- return WifiScanner.REASON_NO_DEV;
+ return WifiScanner.REASON_NO_DEVICE;
case IWifiScannerImpl.SCAN_STATUS_FAILED_INVALID_ARGS:
return WifiScanner.REASON_INVALID_ARGS;
case IWifiScannerImpl.SCAN_STATUS_FAILED_GENERIC:
diff --git a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
index fd595fa..2fa17a1 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
@@ -47,6 +47,7 @@
private ChannelSettings mChannelSettings2;
private HiddenNetwork mHiddenNetwork1;
private HiddenNetwork mHiddenNetwork2;
+ private byte[] mVendorIes;
@Before
public void setUp() {
@@ -59,6 +60,9 @@
mHiddenNetwork1.ssid = TEST_SSID_1;
mHiddenNetwork2 = new HiddenNetwork();
mHiddenNetwork2.ssid = TEST_SSID_2;
+
+ mVendorIes = new byte[]{(byte) 0xdd, 0x7, 0x00, 0x50, (byte) 0xf2, 0x08, 0x11, 0x22, 0x33,
+ (byte) 0xdd, 0x7, 0x00, 0x50, (byte) 0xf2, 0x08, 0x44, 0x55, 0x66};
}
/**
@@ -69,12 +73,12 @@
public void canSerializeAndDeserialize() {
SingleScanSettings scanSettings = new SingleScanSettings();
scanSettings.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY;
-
scanSettings.channelSettings =
new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2));
scanSettings.hiddenNetworks =
new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2));
scanSettings.enable6GhzRnr = true;
+ scanSettings.vendorIes = mVendorIes;
Parcel parcel = Parcel.obtain();
scanSettings.writeToParcel(parcel, 0);
@@ -98,6 +102,7 @@
new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2));
scanSettings1.hiddenNetworks =
new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2));
+ scanSettings1.vendorIes = mVendorIes;
SingleScanSettings scanSettings2 = new SingleScanSettings();
scanSettings2.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY;
@@ -105,6 +110,7 @@
new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2));
scanSettings2.hiddenNetworks =
new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2));
+ scanSettings2.vendorIes = mVendorIes;
assertEquals(scanSettings1, scanSettings2);
assertEquals(scanSettings1.hashCode(), scanSettings2.hashCode());
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 5012622..362eb14 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -68,6 +68,7 @@
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -127,6 +128,9 @@
private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\"";
private static final int[] TEST_FREQUENCIES_1 = {};
private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
+ private static final byte[] TEST_VENDOR_IES =
+ new byte[]{(byte) 0xdd, 0x7, 0x00, 0x50, (byte) 0xf2, 0x08, 0x11, 0x22, 0x33,
+ (byte) 0xdd, 0x7, 0x00, 0x50, (byte) 0xf2, 0x08, 0x44, 0x55, 0x66};
private static final MacAddress TEST_RAW_MAC_BYTES = MacAddress.fromBytes(
new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05});
@@ -512,7 +516,7 @@
SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST));
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
- SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false, null)));
}
/**
@@ -523,12 +527,13 @@
when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
Bundle bundle = new Bundle();
bundle.putBoolean(WifiNl80211Manager.SCANNING_PARAM_ENABLE_6GHZ_RNR, true);
+ bundle.putByteArray(WifiNl80211Manager.EXTRA_SCANNING_PARAM_VENDOR_IES, TEST_VENDOR_IES);
assertTrue(mWificondControl.startScan(
TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER,
SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, bundle));
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
- SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, true)));
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, true, TEST_VENDOR_IES)));
}
/**
@@ -542,7 +547,7 @@
SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, null));
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
- SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false, null)));
}
/**
@@ -556,7 +561,7 @@
SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, new Bundle()));
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
- SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false, null)));
}
/**
@@ -577,7 +582,7 @@
// But the argument passed down should have the duplicate removed.
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
- SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false, null)));
}
/**
@@ -589,7 +594,7 @@
assertTrue(mWificondControl.startScan(
TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_HIGH_ACCURACY, null, null));
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
- IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null, false)));
+ IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null, false, null)));
}
/**
@@ -1161,13 +1166,15 @@
private final Set<Integer> mExpectedFreqs;
private final List<byte[]> mExpectedSsids;
private final boolean mExpectedEnable6GhzRnr;
+ private final byte[] mExpectedVendorIes;
ScanMatcher(int expectedScanType, Set<Integer> expectedFreqs, List<byte[]> expectedSsids,
- boolean expectedEnable6GhzRnr) {
+ boolean expectedEnable6GhzRnr, byte[] expectedVendorIes) {
this.mExpectedScanType = expectedScanType;
this.mExpectedFreqs = expectedFreqs;
this.mExpectedSsids = expectedSsids;
this.mExpectedEnable6GhzRnr = expectedEnable6GhzRnr;
+ this.mExpectedVendorIes = expectedVendorIes;
}
@Override
@@ -1202,12 +1209,15 @@
if (!mExpectedSsids.equals(ssidSet)) {
return false;
}
-
} else {
if (hiddenNetworks != null && hiddenNetworks.size() > 0) {
return false;
}
}
+
+ if (!Arrays.equals(mExpectedVendorIes, settings.vendorIes)) {
+ return false;
+ }
return true;
}